In this blog post, we will explore a common requirement in Sitecore Headless SXA, exposing site-specific settings and global data to the front end when working with a JSS headless site.
Problem Statement
When building headless JSS sites, you often need to expose certain site settings or global configuration data from the Sitecore SXA Settings node to the front-end. However, the out-of-the-box Sitecore Layout Service primarily focuses on delivering route details (placeholders, data sources, fields, etc.), which may not fully cover the broader configuration needs.
In such cases, using GraphQL to query site-specific settings and integrating the results into the page-props is a reliable approach.
The Solution
To address this requirement, we can extend the Sitecore page-props to include site-specific settings. The approach involves:
- Extending the
SitecorePageProps
type to include site settings.
- Creating a GraphQL service to query and fetch the settings items.
- Adding a new plugin to the page-props-factory to populate the site settings into the props for every page.
Let's walk through the implementation step-by-step.
Implementation Steps
Step 1: Extend the SitecorePageProps
Type
The first step is to extend the SitecorePageProps
type to include a new property for site settings. We modify the src/types/props/page-props.ts
file and add a new variable:
1export type SitecorePageProps = {
2 site: SiteInfo;
3 locale: string;
4 dictionary: DictionaryPhrases;
5 componentProps: ComponentPropsCollection;
6 notFound: boolean;
7 layoutData: LayoutServiceData;
8 headLinks: HTMLLink[];
9 SiteConfigurations: SiteSettingFields;
10};
Here, we introduce a new property called SiteConfigurations
of type SiteSettingFields
, which will hold the site settings data.
Step 2: Create a GraphQL Service to Fetch Settings
Next, we create a service to handle GraphQL queries and fetch the settings fields. We’ll create a new file called settings-service.ts
inside src/lib/page-props-factory/services
.
1import { GraphQLClient } from '@sitecore-jss/sitecore-jss';
2import { debug, GraphQLLayoutService, GraphQLLayoutServiceConfig, LayoutServiceData } from '@sitecore-jss/sitecore-jss-nextjs';
3
4export class GraphQLSettingsService extends GraphQLLayoutService {
5 private myGraphQLClient: GraphQLClient;
6
7 constructor(public serviceConfig: GraphQLLayoutServiceConfig) {
8 super(serviceConfig);
9 this.myGraphQLClient = this.getGraphQLClient();
10 }
11
12 public async fetchSettings(siteName: string, language: string): Promise<LayoutServiceData> {
13 const query = this.getQuery(siteName, language);
14
15 const data = await this.myGraphQLClient.request<{
16 sitesettings: {
17 item: {
18 contentRoot: {
19 children: SiteSettingFields;
20 };
21 };
22 };
23 }>(query);
24
25 return data.item.rendered;
26 }
27
28 private getQuery(siteName: string, language: string) {
29 return `query {
30 sitesettings: layout(site: "${siteName}", routePath: "/", language: "${language}") {
31 item {
32 contentRoot: parent {
33 children(includeTemplateIDs: "{54BC031B-293F-46F6-8F98-CB4A9B37BDE2}") {
34 results {
35 sampleField: field(name: "sampleField") {
36 jsonValue
37 }
38 }
39 }
40 }
41 }
42 }
43 }`;
44 }
45}
In this example:
- We use the
GraphQLClient
to execute a query against the Sitecore GraphQL endpoint.
- The query retrieves the children of the settings node, filtering by template ID (
{54BC031B-293F-46F6-8F98-CB4A9B37BDE2}
) and selecting the relevant fields.
- The
sampleField
is an example field from the settings that we want to expose.
This service is designed to fetch the site settings from the settings node, specifically tailored for each site.
Step 3: Add a New Plugin to the Page-Props Factory
Now, we integrate the site settings into our page-props using a custom plugin. We create a file called get-settings.ts
inside src/lib/page-props-factory/plugins
:
1import { GetStaticPropsContext, GetServerSidePropsContext } from 'next';
2import config from 'temp/config';
3import { Plugin } from '..';
4import { GraphQLSettingsService } from '../services/settings-service';
5import { SitecorePageProps } from 'lib/page-props';
6import clientFactory from 'lib/graphql-client-factory';
7
8class GetSiteSettingsPlugin implements Plugin {
9 private settingsRequestClient: GraphQLSettingsService;
10
11 constructor() {
12 const siteName = config.sitecoreSiteName;
13 this.settingsRequestClient = new GraphQLSettingsService({
14 siteName,
15 clientFactory,
16 retries: (process.env.GRAPH_QL_SERVICE_RETRIES && parseInt(process.env.GRAPH_QL_SERVICE_RETRIES, 10)) as number,
17 });
18 }
19
20 order = 2;
21
22 async exec(props: SitecorePageProps, context: GetServerSidePropsContext | GetStaticPropsContext) {
23 const siteSettings = await this.settingsRequestClient.fetchSettings(
24 props?.site?.name,
25 props.locale
26 );
27
28 if (siteSettings != null && props != null) {
29 props.SiteConfigurations = siteSettings ?? {};
30 }
31
32 return props;
33 }
34}
35
36export const getSiteSettingsPlugin = new GetSiteSettingsPlugin();
Plugin Explanation
-
Order Property: The order = 2
ensures that this plugin runs after the core plugins (e.g., normal-mode
and preview-mode
). This is crucial because it allows us to populate the settings data before any layout data is processed.
-
Settings Fetching: We call the fetchSettings
method to retrieve the settings data based on the site name and locale. The resulting data is stored in the SiteConfigurations
property within props
, making it accessible across the application.
Optimization Considerations
Since this GraphQL query runs on every page render, caching the results is recommended to avoid redundant requests and improve performance. Implementing caching at either the service or plugin level can significantly optimize the solution.
Conclusion
This approach gives you a clean and extensible way to expose site settings from the SXA settings node to your Next.js frontend. By integrating GraphQL queries into the page-props factory and leveraging custom plugins, we can make site configurations accessible across every page in the application.
Feel free to enhance this solution further with caching strategies or by extending the settings to include more complex data structures. Happy coding!