Customizing the Layout Service rendering output

Current version: 19.x

When serializing a rendering to JSON, the Layout Service populates the rendering contents with the fields of the rendering's data source item.

Occasionally, you might want the output to include some other types of data, such as:

  • Data from the context item.

  • Data from datasource or context item children.

  • Data from other items altogether.

  • A more limited view of any of the previously mentioned types of data to avoid over-fetching unneeded data.

  • A computed or otherwise more complex value.

  • Non-item data, such as information from xDB.

  • Non-Sitecore data from external systems.

The following sections describe some of the options for customizing the rendering output returned by the Layout Service.

Using the GraphQL API

You can customize the Layout Service output for a Sitecore rendering by configuring a GraphQL query on the rendering. In JSS, this technique is referred to as integrated GraphQL. The query runs server-side during the Layout Service request, or at publish time if using Experience Edge.

When the Sitecore Layout Service renders a page, it returns a JSON representation of the layout of the page and the data for each rendering/component. Normally the rendering/component data is a set of fields from the Sitecore datasource item. Integrated GraphQL lets you re-shape this into a GraphQL query result.

To use this technique, you must:

  • Define a Sitecore GraphQL endpoint that has the schema you want to query.

  • Configure your application to use the endpoint. For example:

    RequestResponse
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
      <sitecore>
        <javaScriptServices>
          <apps>
            <!-- you may override other attributes from 'defaults' in the app definition below -->
            <app name="JssBasicAppGraphQL"
                 graphQLEndpoint="/sitecore/api/jssbasicappgraphql"
                 inherits="defaults"
            />
          </apps>
        </javaScriptServices>
      </sitecore>
    </configuration>

Integrated GraphQL operates by storing the GraphQL query in the Component GraphQL Query field on the component's rendering item. For example, the GraphQL sample app sets a query on /sitecore/layout/Renderings/JssBasicAppGraphQL/IntegratedPage. For example:

RequestResponse
query IntegratedPageQuery(
  $datasource: String!
  $language: String!
  $contextItem: String!
) {
  datasource: item(path: $datasource, language: $language) {
    ... on IntegratedPage {
      title {
        jsonValue
      }
      text {
        jsonValue
      }
      logoImage {
        jsonValue
      }
    }
  }

  contextItem: item(path: $contextItem, language: $language) {
    id
    children(hasLayout: true) {
      results {
        displayName
        url {
          url
        }
      }
    }
  }
}
    
Note

For the default and jss layout service configurations, values for $datasource, $contextItem, and $language are automatically injected.

When Sitecore receives a layout request, if a component defines a GraphQL query that is not empty, the query is run against the GraphQL endpoint configured for the app, in-process. The result replaces the normal rendering field values that are returned.

Choosing or configuring a built-in Rendering Contents Resolver

Headless Services lets you configure a Rendering Contents Resolver on each rendering, to determine how a rendering and its associated data are serialized. Rendering Contents Resolvers are configured in /sitecore/system/Modules/Layout Service/Rendering Contents Resolvers. By default, Headless Services provides the following resolvers:

  • Datasource Resolver - the default behavior. It serializes the rendering's datasource item.

  • Datasource Item Children Resolver - serializes the children of the datasource item.

  • Context Item Resolver - serializes the context item instead of the datasource item.

  • Context Item Children Resolver - serializes the children of the context item.

  • Folder Filter Resolver - serializes the descendants of the datasource item, excluding folders.

You can create your own configuration in the Rendering Contents Resolvers folder using the following available parameters:

  • Type - this is Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver, Sitecore.LayoutService, unless you are creating your own implementation.

  • Include Server URL in Media URLs - always check this field unless you run a front-end app built with JSS in Integrated mode.

  • Use Context Item - use the context item instead of the datasource item.

  • Item Selector Query - provide a Sitecore query to customize the serialized items. This must be relative to the datasource and/or context item, depending on the selection above.

    Important

    When using this parameter, be aware that it is easy to create Sitecore queries with a severe negative performance impact.

  • Parameters - provide arbitrary parameters. These are not used by default but are potentially useful when creating your own implementation.

To use the Rendering Contexts Resolver, you must set it in the Rendering Contents Resolver field, on the rendering item that references the component it applies to. For an example, refer to this walkthrough that sets resolvers for Form rendering when integrating Sitecore Forms with JSS.

Creating an IRenderingContentsResolver interface

The Layout Service lets you fully customize the contents of a serialized rendering with the help of the IRenderingContentsResolver interface.

Important

Whenever possible, for future compatibility, we recommend you use one of the previous no-code options.

You can override the default IRenderingContentsResolver interface on a rendering by creating your own implementation and creating a Rendering Contents Resolver item, specifying your custom Type. For example:

RequestResponse
public class ExampleRenderingContentsResolver : Sitecore.LayoutService.ItemRendering.ContentsResolvers.IRenderingContentsResolver
{
    public bool IncludeServerUrlInMediaUrls { get; set; }
    public bool UseContextItem { get; set; }
    public string ItemSelectorQuery { get; set; }
    public NameValueCollection Parameters { get; set; }

    public object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
    {
        //if you want to access the datasource item
        var datasource = !string.IsNullOrEmpty(rendering.DataSource)
            ? rendering.RenderingItem?.Database.GetItem(rendering.DataSource)
            : null;

        return new
        {
            name = datasource.Name,
            date = DateTime.Now,
            hello = "world"
        };
    }
}

The fields of the object returned by your implementation can then be bound to your front-end components as if they were item content (for example, props in React).

Tip

You can extend the Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver class and use the ProcessItem method from the class to enable rendering item fields for advanced Sitecore editors.

The advantages of using an integrated GraphQL query instead of a separate REST endpoint

For some of the scenarios above, an alternative approach might be to create a separate REST endpoint to access the needed data. However, the previously described techniques offer several advantages compared to using a REST endpoint, such as:

  • No additional HTTP roundtrip(s).

  • Automatic binding of the data to the component.

  • Data is available for server/universal rendering.

  • Easier querying additional data related to the current application, context item (route), or datasource item.

Note

There is some overlap in potential use cases between using an integrated GraphQL query and extending the context data returned by the Layout Service.

Integrated GraphQL queries ensure the data is made available when, and only when a content author utilizes that rendering within a route.

Extending the context data returned by the Layout Service is generally intended for information that might be used in multiple components, or for providing data for statically placed components not managed within a Placeholder.

Do you have some feedback for us?

If you have suggestions for improving this article,