How to Make Exciting Xperience Websites with New Static Site Generators
Static site generators, which seem to have become more and more popular recently, could be one of the ways of increasing Kentico Xperience's performance, but performance is just one of many benefits. We can often read articles stating that “static sites are not for everyone”. In this article, we will investigate whether this is true in the context of Kentico Xperience. This article aims to show how static site generators could be integrated with Kentico Xperience and what the best use cases are.
Let’s start with a basic definition of what static site generators are, by referring to how sites are created. Most, if not all modern sites, are dynamically generated where resulting HTML code displayed to reader in a browser is compiled through resources costly process. Static site generators skip this generation process and instead of taking multiple variables and creating the final HTML code, static site generators create a final code and keep it cached. When a request for the page comes it is just “pulled out” from the memory and sent over to the user to be displayed. No expensive scripting is happening.
This approach has several benefits:
-
Speed – dynamic sites go back to the server to render each page every single time someone visits it while static site generators store a “pre-built” version that can be delivered nearly instantly. With static site generators, you can save a lot of this back and forth communication and can achieve a substantial speed increase.
-
Fewer moving parts to maintain – CMS platforms require a surprising amount of interconnected software and hardware to keep them up and running: a machine that runs Linux, a web server that runs Nginx or Apache, a scripting language (with associated extensions and web server configurations), SQL database, CMS system, Various plugins and add-ons. On the other hand, you can host a static site generator on pretty much any server that returns HTML files. With fewer moving parts to keep updated and secure, you can pretty much set it and forget it for a while.
-
Overall server performance improvements – fewer moving parts undergoing a traffic spike require fewer resources, which improves robustness and stability.
-
Security – the server doesn’t have to build the page completely from all the assets and there’s a lower opportunity for hackers to inject malicious code or exploit your database.
In the case of Kentico Xperience, we have tried Gatsby and Statiq. Let’s see in what ways they are the same and in what ways they differ.
Gatsby
One popular static site generator is Gatsby. A typical Gatsby site consists of two parts: a data layer using GraphQL, and the presentation layer. GraphQL is a query language which allows you to specify only what you want to receive in the data. The presentation layer is a JavaScript app written in React. Each part can be independently part of either a dynamic site or a static site. Gatsby combines these two parts at build time, producing static HTML files that a server can serve. Gatsby provides a cloud server for this purpose, with flexible plans, called Gatsby Cloud.
To integrate Kentico Xperience 13 with Gatsby, only the GraphQL part needs to be configured. This can be done with a source plugin, which allows you to add a GraphQL node to the existing nodes available in Gatsby. This GraphQL node can have any schema, so we created a schema based on the site object in Kentico Xperience 13 and its logical children. This is a partial view of the schema:
As shown by this partial view, a site has page and menu nodes and some fields, a page has children, media, links, attachments, and some fields, and an attachment has just fields. Apart from the site, there is also a localize node, which allows you to query resource strings.
The Kentico Xperience 13 GraphQL node is served by a separate GraphQL server which acts as a web farm server. This server runs using Hot Chocolate, which is part of the ChilliCream suite of .NET GraphQL tools. When Gatsby builds, it queries this server for the GraphQL schema and runs any GraphQL queries against it that it needs to complete building the site. This server also refreshes the Gatsby site using Gatsby’s refresh endpoint whenever any Kentico Xperience 13 content changes.
The React part of the Gatsby integration is a simple React application that only needs to know the Kentico Xperience 13 GraphQL server details and the Kentico Xperience 13 GraphQL schema. Typically, a query would use aliases to query named nodes, like this:
{
site(name: "DancingGoatCore") {
home: page(path: "/Home") {
name
bannerText: field(name: "BannerText")
banner: media(field: "Banner") {
url
}
}
}
}
Here, home, bannerText, and banner are aliases for their respective nodes. Any issues with the GraphQL queries are reported at build time. If the site builds, then all the content was retrieved successfully.
Once the Gatsby site was complete, we wanted to compare the performance of the site with a similar site running on .NET Core (without output caching). We set up a test using k6 including 100 and 500 concurrent users. The pages visited in the performance test were a home page and some article pages. All pages had a mixture of text and image content. The Gatsby site, running on Gatsby Cloud, performed a little better across the board. While not part of the test, making a change in the Kentico Xperience 13 administration application caused the Gatsby site to rebuild, and a few seconds later the change was available to visitors of the site.
When working with GraphQL, it is important to be aware of the schema available to you. We recommend either using Gatsby’s built-in GraphiQL schema explorer to write and test queries, or using Banana Cake Pop, the GraphQL IDE that runs with Hot Chocolate. The important part of knowing the schema is knowing which nodes are available under a node, and whether a field is an Object type or a simpler type, such as a String.
Another recommendation is to be familiar with the content structure of the Kentico Xperience 13 site itself. This includes knowing page paths, field codenames, page type codenames, and the data type of fields. That way you can quickly write queries knowing you are querying the right content and that you will receive the content you expect.
The biggest pro of using Gatsby is that it uses GraphQL, which reduces all the content data needed for a page down to a single request, and the data response contains only what was requested. This means faster querying and less information over the network, and no wasted information. It is important to mention that assets, such as images, are still downloaded at visit time. The GraphQL response only contains a link to each asset.
The biggest con of integrating Gatsby with Kentico Xperience 13 is that it cannot use the powerful page builder feature because there is no .NET front end to show it. This means that all content has to be structured content and contained in page type fields or child pages. This can degrade editor experience and may mean the site will not benefit from future enhancements to the page builder feature.
Statiq
Statiq is a rising star in the .NET SSG community. Originally known as WYAM, it is the most complete and user-friendly .NET Core SSG being developed today. Statiq websites are generated by running a simple .NET Core Console application which uses templates such as Razor and Handlebars to create the final HTML for your site.
Statiq uses the concepts of Pipelines and Modules to process input data and render files. Pipelines are registered during the application initialization and contain Modules that define their functionality at different phases of Pipeline execution.
Thanks to Kentico’s .NET API provided by the Kentico.Xperience.Libraries NuGet package, integrating Kentico Xperience and Statiq couldn’t be easier. To make the integration even easier, we started by creating several base classes designed for retrieving data from your Kentico Xperience database. For example, the simple Kentico XperienceContentModule can be included in any Pipeline to retrieve strongly-typed documents from your content tree. As Statiq expects its own IDocument object at various points during processing, we’ve also created the Kentico XperienceDocumentConverter to aid in IDocument conversion when needed.
With this new Module developed, we can include it in our Pipelines to retrieve pages from Kentico Xperience. We created another base class Kentico XperienceContentPipeline with our Module executing in the Input phase of the Pipeline, so retrieving pages is as simple as writing a DocumentQuery:
class AuthorPipeline : XperienceContentPipeline<Author>
{
public AuthorPipeline()
{
Query = AuthorProvider.GetAuthors();
}
}
If we include this Pipeline in our Program.cs at startup, we will have access to all Author pages from our content tree:
return await Bootstrapper
.Factory
.CreateDefault(args)
.AddPipeline<AuthorPipeline>()
The core of any SSG is obviously static file generation, but this Pipeline doesn’t generate any files, so what is it for? Well, Pipelines have access to the output data of other Pipelines from the Input phase, so let’s take a look at a more complicated example which compiles that data from multiple Pipelines and generates static HTML:
There’s quite a bit going on here, so let’s break it down:
- Dependencies: This Pipeline requires data from other Pipelines such as AuthorPipeline, so we need to make sure it runs after those Pipelines have executed their Input phase
- ReadFiles: Here we are specifying a Razor template that will be processed to create the final HTML
- WithModel: We can optionally provide a custom ViewModel to our Razor template for processing. Here, we gather the data from our other Pipelines, convert them into Kentico Xperience object types, and create a custom HomeViewModel
- SetDestination: The desired path of the fully-generated HTML (in the /output folder)
- WriteFiles: Performs the actual writing of the static HTML
Now when we run the Console application, we have our index.html file which can be deployed wherever we need it such as Netlify or GitHub Pages using a GitHub Action or Statiq methods.
To rebuild the website when content changes in the Kentico Xperience CMS, you can trigger a workflow_dispatch of your GitHub Action. There are many ways to accomplish this within the Kentico Xperience application such as global events, but for pages in your content tree, you may also choose to create a custom workflow step.
By now, it should be clear that developing a static website with Statiq is a breeze—because it is a .NET Core application, you can start developing with the Kentico Xperience API without the need for additional complexity. You gain all the benefits of a static website including blazing speed and automatic rebuilds with little development effort.
As with other SSGs, one drawback is the inability to gracefully handle user interaction such as forms. You can use the Kentico Xperience REST service to manipulate data from the front end, but as the functionality is contained within JavaScript files, you may expose credentials and endpoints to the public.
In conclusion...
Static site generators such as Gatsby or Statiq are a great way to gain performance, stability, and security benefits for your project. They reduce development costs and complexity and simplify the application architecture. Despite some drawbacks such as inoperability with some Kentico Xperience features, static site generators are fully capable of displaying Kentico Xperience content and working with Kentico Xperience data.
For a deeper dive into the code of both implementations, here are links to their GitHub repositories:
Gatsby Statiq