XM Cloud Renderings - Component GraphQL Query
So you have Sitecore XM Cloud up and running locally and you want to make a new component. Lets say something simple like a links component or a social media component. The easiest way is to duplicate one of the out of the box components.
Lets take a look at the LinkList.tsx
component as an example. Make a copy of the file and name it IconLinkList.tsx
. It’s a fairly typical Sitecore component with the following template definitions:
The Link List Folder
contains the Link List Root
which then contains the Link(s)
. Lets see if we can update the Link
to support an icon as well as the link itself.
Just like in a traditional Sitecore implementation the first step is to add the field to the template. In this case I’ve added the Icon Name
field as a text field.
I’ve also updated the content item to fill in the new icon name.
As you look through component’s tsx file you’ll notice that most of the file is just setting up types and interfaces. It’s basically just defining what the data is going to look like for the component.
The entrypoint of the component starts here:
export const Default = (props: LinkListProps): JSX.Element => {
const datasource = props.fields?.data?.datasource;
const styles = `component link-list ${props.params.styles}`.trimEnd();
const id = props.params.RenderingIdentifier;
You can see that the datasource of the component is being passed in as its props and that the props are typed earlier in the code. Now lets say you want to add another field to the data source. It should be easy right? That’s where things get interesting. The types defined in this file need to match the data coming from Sitecore.
So how does that work exactly? It’s not like it’s one item to one datasource. There’s a hierarchy of items in that data source. It starts with the Title text field on the parent item. All of the links are children of that parent item.
The glue that puts that datasource together is in the rendering. The rendering contains a field called Component GraphQL Query
. It contains a query that pulls data from the root item with the Title text field and all of the links.
query TitleQuery($datasource: String!, $language: String!) {
datasource: item(path: $datasource, language: $language) {
children {
results {
field(name: "Link") {
link: jsonValue
}
}
}
field(name: "Title") {
title: jsonValue
}
}
}
So this is where the trickiness begins. My first thought was to just add a new duplicate field to the results object above with the new Icon Name
field and call it a day as everything just worked. It failed spectacularly.
At first glance everything looks fairly decoupled but in reality these existing components are tightly coupled. The biggest constraints are getting the shape of the output object to match the type defintions in the component. Those definitions then need to be compatible with the Jss components as well. In the end I needed to restructure the query a bit so that it looked like this:
query TitleQuery($datasource: String!, $language: String!) {
datasource: item(path: $datasource, language: $language) {
children {
results {
field: field(name: "Link") { link: jsonValue}
iconName: field(name: "Icon Name") { value: value}
}
}
field: field(name: "Title") { title: jsonValue }
}
}
The type definitions in the component ended up changing to this:
type ResultsFieldLink = {
field: {
link: LinkField;
};
iconName: TextField;
};
type FooterSocialMediaItemProps = {
key: string;
index: number;
total: number;
field: LinkField;
iconName: TextField;
};
With that everything works as expected.
References: