One of the biggest draws to using SXA is the ability to use Variants. Give your authors some “variation” as to how the components you’ve built can be used. While this has been great, it wasn’t really available for the NextJs Headless architecture. With 10.3 and XM Cloud however, all that has changed and we get to enjoy SXA in all its glory, not just with variants.
Example: Light and Dark Variant in Headless SXA
For the purposes of this example I’m going to create a light and dark version of the Heading component. The light version being black text on a white background and the dark being white text on a black background.
We also want to ensure that when a component is added to the page, if no datasource is added to it, that it doesn’t break Experience Editor.
Like so:
Sitecore Items
How do we achieve that? Well first, let’s create our Heading variant structure. Under our Site node, let’s go to /Presentation/Headless Variants
and create a variant with the same name as our tsx
file. In our case, it’s Heading.tsx
. Under that we will create a Variant Definition
called Default
, and then another called DarkHeading
. We can use Display Name to make these more readable later but these two will match the name of the functions within our Heading.tsx
file.
It is vital that one is called Default
.
The Code
For the code itself, it’s broken down into three main functions.
First we have Default
export const Default = (props: HeadingProps): JSX.Element => {
if (props.fields) {
return (
<div className="flex w-full">
<div className="flex p-7 w-full">
<Text
tag="h1"
className="relative font-black text-6xl lg:text-7xl leading-tight"
field={props.fields.heading}
/>
</div>
</div>
);
}
return <HeadingDefaultComponent />;
};
Then we have DarkHeading
export const DarkHeading = (props: HeadingProps): JSX.Element => {
if (props.fields) {
return (
<div className="flex w-full">
<div className="flex p-7 text-white bg-black w-full">
<Text
tag="h1"
className="relative font-black text-6xl lg:text-7xl leading-tight"
field={props.fields.heading}
/>
</div>
</div>
);
}
return <HeadingDefaultComponent />;
};
And you’ll notice that both of these contain a fallback if no datasource is present.
export const HeadingDefaultComponent = (): JSX.Element => (
<div className="flex component">
<div className="component-content">
<span className="is-empty-hint">No Datasource Present</span>
</div>
</div>
);
The entire file itself, looks like this:
import { Text, Field } from '@sitecore-jss/sitecore-jss-nextjs';
import { ComponentProps } from 'lib/component-props';
type HeadingProps = ComponentProps & {
fields: {
heading: Field<string>;
};
};
export const HeadingDefaultComponent = (): JSX.Element => (
<div className="flex component">
<div className="component-content">
<span className="is-empty-hint">No Datasource Present</span>
</div>
</div>
);
export const Default = (props: HeadingProps): JSX.Element => {
if (props.fields) {
return (
<div className="flex w-full">
<div className="flex p-7 w-full">
<Text
tag="h1"
className="relative font-black text-6xl lg:text-7xl leading-tight"
field={props.fields.heading}
/>
</div>
</div>
);
}
return <HeadingDefaultComponent />;
};
export const DarkHeading = (props: HeadingProps): JSX.Element => {
if (props.fields) {
return (
<div className="flex w-full">
<div className="flex p-7 text-white bg-black w-full">
<Text
tag="h1"
className="relative font-black text-6xl lg:text-7xl leading-tight"
field={props.fields.heading}
/>
</div>
</div>
);
}
return <HeadingDefaultComponent />;
};
We’re now able to use and select the appropriate variant depending on our need.
I’m sure you’ll be able to come up with a better use for variants but as you can see, they’re quite handy and very powerful.