@sitecore/components
v2.0.1
Published
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.
Downloads
51,575
Keywords
Readme
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 FEAAS.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.
Structure thesis
- Each folder in
src
represents a component - Each component needs to be included into
index.ts
, so that its type augmentations are bundled properly - Each component needs to be registered in package.json as conditional export (for new npm modules)
- Each component will be copied to root on build for backward compatability with pre-module npm
- Each component is imported as side-effect without destructuring e.g. import
import '@sitecore/components/form'
- It's acceptable to group components by subfolder, e.g.
@sitecore/components/search/input
- Each component/component group must be imported by user individually
npm run build
does all the work
Guidelines
1. Each component must be exported individually:
This is so users can choose what they want to use.
// Somewhere in the user app
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.
It is acceptable for a sitecore team to group their components under a subfolder (e.g. @sitecore/components/search/input
)
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. If a component uses a one-off SDK, consider loading it from CDN on demand so it doesnt become a part of user app if component is not used directly on the page.
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. Since BYOC components are regular React components. However it's important to keep the side-effect import in place.
Example of rendering a component directly:
// keep the side-effect import in the app to ensure the unconditional bundling
import "@sitecore/components/form";
// destructure as a separate import
import { Form } from "@sitecore/components/form";
// use it in the app
const App = () => <Form formId="my-form-id" />;
Example of rendering a component through a wrapper:
// import the necessary component
import "@sitecore/components/form";
// import FEAAS clientside
import * as FEAAS from "@sitecore-feaas/clientside/react";
// use it in the app
const App = () => (
<FEAAS.ExternalComponent componentName="form" formId="my-form-id" />
);
Authoring
See @sitecore-feaas/clientside readme for more information about component registration under Bring your own components section.
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:
- Bundle component that lists all BYOC components that need to be a part of clientside js - note that it uses a special kind of import “for side-effects”, that ensures that code will be a part of the bundle regardless of if it’s used or not.
- A place where this bunde is included
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.
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.
- 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).