Sitecore BYOC components
A repository of react components authored by Sitecore that offers integration of different products in an
easy-to-consume package. The promise of it is that it's mostly plug-and-play business.
What is BYOC?
BYOC is a way to register react components into Pages/Components app from within User app. It’s a streamlined system
that makes it easy to add functionality in a way that is familiar to the regular developer.
Using BYOC requires two steps:
- Defining a react component (regular everyday react component, not sxa)
- Registering that component (using BYOC.registerComponent call).
Use cases
BYOC components serve two audiences, and solve two problems:
User own components
In this the component is defined and registered from within the User app
(example).
Sitecore does not really have access to the source and definition of those components, and yet they appear in the UI as
if they were native. It’s really good way for the users to extend their Sitecore websites, as they can use any tools,
dependencies and techniques. They can choose to re-use their components, or create one-offs specific to the app. It goes
as far as supporting hot-reloading their code right in the browser within the context of developer’s next.js render
host.
Sitecore components
The other large use case for BYOC is to allow Sitecore products to be integrated together easily, using a shared
mechanism allows making changes easier. It shortens the release cycle, and de-risks the new additions to the ecosystem.
Unlike user components, the sitecore components are imported from an npm package. New components become an opt-in to the
user app, the user only needs to update the version of the npm package, and import what they need in their app.
Modes of operation
BYOC components attempt to offer solutions for most of the developer needs, beyond what’s typically expected.
Essentially it’s all different combinations of rendering component on server or client-side.
Rendered on client
The basic use case is a component that adds interactivity on the page, such as smart input field, or a tool to display
some dynamic content (e.g. sitecore forms). In this case, the component has to be loaded on the clientside. In next.js
it may not be so straighforward, as it tends to optimize out the components that aren’t used. Additionally, in app
router setup of next.js 13, all components are by default considered server side, so clientside components need to be
opt-in. The solution to this is to define a specia bundle component that lists all the components that are expected to
work on clientside, and then placing that component into the layout of an app. This ensures that the code is loaded to
the client correctly. See example:
Rendered on server
Other class of components is rendered on server. This allows the output of the component be indexable by search engines,
and enables fully static websites that have very little clientside javascript.
There are two variations of these:
- Old-style server component - a one that can not use hooks like useEffect, but can output JSX. All the dynamic
data fetching must happen on the page level. This is an approach JSS is taking for BYOC components.
- New async server components - a type of server-only component that can have its own asynchronous logic (e.g.
fetching data, calling apis, etc), making it very powerful and easy to use. It is a great way to mak make secure
requests to databases, services and apis without exposing the secrets and credentials to the clientside app. The
downside is that it requires next.js 13 app router style of application. This will not be available in JSS apps for
now.
Example
Server components need to be imported somewhere in the app,
e.g. in layout.
Notice that it’s a side-effect import (i.e. it does not destructure the exports). This is a special way to import that
opts the code out of tree-shaking. This is important, as next.js app does not know which components are used in the
layout tree or feaas component, so it should not try to optimize them away.
Hybrid component
A component that renders on server and later gets hydrated on the clientside is pretty popular too. It allows the page
to be indexable, and it makes user see parts of the design before app is fully initialized. It’s a good idea to try to
use this style of components wherever possible. For this to work, the component needs to be imported both in
client side
and
server side
of the next.js bundle.
Example
Swappable component
Another way to combine two components is that server side renders something different from the clientside. It could be a
placeholder, or empty state of a component, that gets replaced with a interactive one as soon as the page loads. Since
it’s a variation of hybrid omponent, it also requires registering 2 components, one on the server and one on the
clientside.
Example
Wrapper component
In apps that support app router and async components, it is possible to create combinations of async and sync
components. For
example
async server component fetches data and passes it to clientside component to do something with it. Since there’s server
and client component involved, it requires each of those to be registered on server and in client side bundles
separately.
Guidelines
1. Each component must be exported individually:
This is so users can choose what they want to use.
import '@sitecore/components/form'
import '@sitecore/components/search'
Keep in mind this special side effects
import syntax, which opts out of code tree-shaking. It means the components
will be included in the user app, regardless of if they are used in the page or not. The reason for this is that XM
and FEAAS components both are rendered dynamically, so the tree-shaking algorithm can not possibly do a good job.
2. There's no bundling, only typescript transpilation
This is to avoid double bundling of different versions of the shared libraries. Adding dependencies to the package is
OK. All of the dependencies will be installed into the user app, but they wont be bundled into their code unless
component is actually used. This is why it is important
Keep dependencies to the minimum to avoid bloat.
3. Components can be rendered directly
Usually BYOC components are rendered as a part of XM page or FEAAS component, so there's no need to refer to them
directly. But if there's a need to render a component manually (e.g. in Layout of an app), it can be possible to render
the the components directly. However it's important to keep the side-effect import in place.
Example of rendering a component directly:
import '@sitecore/components/form'
import {Form} '@sitecore/components/form'
;<Form formId='my-form-id' />
Example of rendering a component through a wrapper:
import '@sitecore/components/form'
import * as BYOC from '@sitecore-feaas/byoc'
;<BYOC.Component componentName='form' formId='my-form-id' />
Authoring
See
@sitecore-feaas/clientside
readme for more information about component registration under Bring your own components section.
Thoughts from Yarik
- Any sitecore team can now release changes almost directly to customer, without needing to go through the pains
of integration with Pages, JSS, XMC, SXA, Base packages, starter kits, etc. It also potentially enables
pre-releasing the new functionality to before announcing it officially. It could cut down the time-to-market 10
times and more.
- Huge limitations: JSS does not support app router. Not supporting async server components is a huge limitation
to BYOC potential. Without it, any asynchronous logic like data fetching needs to happen on clientside or as a part
of JSS-specific code that needs to be pulled in to the shared jss package.
- Sitecore components are not a part of user app. They should come from npm package, or else we risk to repeat
mistakes of JSS generated components that are hard to update by the user.
- BYOC.registerComponent() call needs to happen in the same file where the component is defined. This is because
registration defines the JSON schema for input parameters of the component. It must not de-desync with component
definition, and needs to be authored in place. This means, that package that contains the sitecore BYOC component
depends on
@sitecore-feaas/clientside
for registration - Adding a sitecore BYOC component to the app is done through import call. Knowing that we have essentially 2
bundles of code in the next.js app - server and client, we need to allow user to choose where to register the
component in a way that next.js understands (see above “Rendered on client” above). Adding import automatically
registers the component.
- Sitecore components should be importable individually from separate files. Barrel-style files listing all
components should be avoided, so that unnecessary code is not pulled into the app. The way we do anonymous imports,
tree-shaking will not really to avoid loading unwanted code. It should be as easy as “import
@sitecore/components/form”. Each of those imports may bring its own dependencies, but only if it’s actually used in
the app.
- The package that makes sitecore components available should not be a part of some other larger package. This is
to ensure that each component can be imported individually, and that both FEAAS and the component package can be
updated individually and pinned to specific versions. It still can be possible to make it a dependency of that
larger package if necessary.
- We should not have any temporary storage for the components. We need to start a new package ASAP, so that we
avoid the pain of future migrations. If @sitecore/components is not available, we will use
@sitecore-feaas/components instead. If we put it in JSS, we’ll be stuck with it.
- All sitecore components should be easily available without reading docs or npm installs. The user should not
need to install that package, it will be a depdency of the app. Some of those may be pre-imported in the newly
generated app, while being easily removable (simply remove the import calls).
- JSS needs to update BYOC usage to match the provided example. The
github repo
shows a correct way to use BYOC components that works in all scenarios, especially clientside. It includes creating
a clientside bundle file for clientside components, and passing
dynamic
to a FEAAS function. - Future possibility: moving component registration into component repo. Currently registerComponent function is a
part of @sitecore-feaas/clientside. We could make it be a part of the @sitecore/components instead, leaving backward
compatability in place. This would allow us to decouple the BYOC from FEAAS.