Jamstack on .NET: From zero to hero with Statiq and Kontent
Tags: jamstack, dotnet, webdev, csharp
Published: 2020-11-30 11:21:13

**Are you interested in Jamstack, but sad you can't practice it with .NET? The times have changed, and .NET developers are now able to build Jamstack apps using the new static site generator Statiq. This article walks you through the setup of a ready-to-use boilerplate! If you are new to Jamstack on .NET, follow the steps below to wrap your head around the basics. If you know the drill, skip that part and use the boilerplate.**

{% github Kentico/kontent-boilerplate-statiq-net %}

## Technologies

Let's briefly introduce [Jamstack](https://jamstack.org). It is a web architecture based on the pre-rendered assets (HTML files, images, CSS, etc.) that are generated at the build time based on templates and data.

![Technologies puzzle](https://dev-to-uploads.s3.amazonaws.com/i/teslw1iuy256t4fa8fvc.png)

Now, let's jump right to the available technologies. First is Statiq, a generation platform providing the .NET based [Statiq.Framework](https://statiq.dev/framework) for creating a static site generator infrastructure and a set of modules called [Statiq.Web](https://statiq.dev/web) that will make the initial configuration of the framework easier.

The next pieces of the puzzle are the **templates and data storage**. I have chosen **Razor as a template engine** since it is the most known one in the .NET world. **Data** will be provided by a **headless CMS—Kentico Kontent**. It provides more flexibility and collaboration features in comparison with markdown files, and it has a Statiq module out of the box.

The last piece is to get all the assets and [deploy them to Netlify.](https://statiq.dev/web/deployment/netlify)

## Let's start

The whole boilerplate creation is split into three parts—an application skeleton, a content source, and a connector that allows Statiq to generate pages.

_Before you do anything else,_ [_download and install a .NET 5_](https://dotnet.microsoft.com/download) _if you haven't already._

### Prepare the application

First, use [dotnet CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new) to create a new console application.

```powershell
dotnet new console --name FromZeroToHero
```

Then, navigate to the application folder and install a `Statiq.Web` Nuget package (which will also download `Statiq.Framework` as a dependency).

```powershell
cd FromZeroToHero
dotnet add package Statiq.Web -v 1.0.0-*
```

And then configure `Statiq.Framework` in `Program.cs` to use the default configuration of the `Statiq.Web` modules.

```csharp
using System.Threading.Tasks;
  using Statiq.App;
  using Statiq.Web;

  namespace FromZeroToHero
  {
    public class Program
    {
      public static async Task<int> Main(string[] args) =>
        await Bootstrapper
          .Factory
          .CreateWeb(args)
          .RunAsync();
    }
  }
```

💡 _At this point, if you run_ `dotnet run -- preview` _, you can browse your empty site at_ `http://localhost:5080` 💡

### Prepare content

As the title mentioned, the boilerplate is about going "from zero to hero". This means creating a "Hero" content model that consists of a headline, summary, and CTA. Plus creating one content item based on that model. We'll do all that in a headless CMS [Kentico Kontent](hrrps://kontent.ai) as it already features Statiq integration.

* First, [create an account at Kontent.ai](https://app.kontent.ai/sign-up?utm_source=nextjs_docs_example&utm_medium=devrel&utm_campaign=extended_trial).

  - _The link above will provide you with a 90-day trial. Once you finish the trial, or even during the trial period, you can switch to the_ [_Developer plan_](https://kontent.ai/developer-plan) _, which is_ _free of charge_ _and lets you run small projects with a $0 budget._
* After signing up, [create an empty project](https://docs.kontent.ai/tutorials/set-up-kontent/projects/manage-projects#a-creating-projects).
* From your Kontent project, go to _Content models_ and add a new Content type called "Hero" with these text fields:

  - Headline
  - Summary
  - CTA label
  - CTA URL

![Hero model](https://dev-to-uploads.s3.amazonaws.com/i/aihx7797ess1e36jrtqj.png)

* Go to _Content & Assets_ section in your project and click _Create new_ on the _Content_tab and select _Hero_ content type and fill the elements

  - _Content Item Name:_ Hero
  - _Headline:_ From zero to hero with Statiq and Kontent
  - _Summary:_How to start with the .NET Jamstack app utilizing Statiq as a Static generation platform and Kentico Kontent as a data source.
  - _CTA label:_ Explore
  - _CTA URL:_ https://github.com/Kentico/kontent-boilerplate-statiq-net
* Publish the changes

![Hero item](https://dev-to-uploads.s3.amazonaws.com/i/a61ilshcqgylk7tm353b.png)

### Connect content and the site

Now let's load the content from the headless CMS using [Kontent.Statiq](https://www.nuget.org/packages/Kontent.Statiq) module and render it with the Razor engine.

First, install Kontent.Statiq module:

```powershell
dotnet add package Kontent.Statiq --version 1.0.0-*
```

Copy the Project ID from the Kontent application.

* Enter [Kontent application](https://app.kontent.ai/)
* Go to "Project Settings" and select "API keys"
* Copy "Project ID"

![Prikect ID selection](https://dev-to-uploads.s3.amazonaws.com/i/ibskvxxmym5nybclgbxn.png)

Then install the [Kontent Model Generator for .NET](https://www.nuget.org/packages/Kentico.Kontent.ModelGenerator) and generate our Hero model.

```powershell
dotnet new tool-manifest
  dotnet tool install Kentico.Kontent.ModelGenerator
  dotnet tool run KontentModelGenerator --projectid "<projectid>" --namespace "FromZeroToHero.Models" --outputdir "Models" --generatepartials true --structuredmodel true
```

_For more specific information about the model generator configuration,_ [_follow the generator's Readme_](https://github.com/Kentico/kontent-generators-net#readme) _._

After generation, you will end up with three files `Models/Hero.Generated.cs`, which is a POCO model class, then `Models/Hero.cs` that allows extending the model class, and the last one is `Models/CustomTypeProviders.cs` that will be used to provide model mapping information.

The next step is to initialize the Kontent Delivery SDK client for dependency injection. Extend `Program.cs` file with `ConfigureServices`.

```csharp
using System.Threading.Tasks;
  using FromZeroToHero.Models;
  using Kentico.Kontent.Delivery.Abstractions;
  using Kentico.Kontent.Delivery.Extensions;
  using Microsoft.Extensions.DependencyInjection;
  using Statiq.App;
  using Statiq.Common;
  using Statiq.Web;

  namespace FromZeroToHero
  {
      public class Program
      {
          public static async Task<int> Main(string[] args) =>
            await Bootstrapper
              .Factory
              .CreateWeb(args)
              .ConfigureServices((services, config) =>
              {
                  // Add the type provider
                  services.AddSingleton<ITypeProvider, CustomTypeProvider>();
                  // Configure Delivery SDK
                  services.AddDeliveryClient(opts =>
                      opts.WithProjectId("<projectid>")
                          .UseProductionApi()
                          .Build());
              })
              .RunAsync();
      }
  }
```

All of your assets and templates should be placed in the `/input` folder. Create a view file `_Hero.cshtml` in the `/input` folder then and use the generated `FromZeroToHero.Models.Hero` class as a model for the template.

```html
@model FromZeroToHero.Models.Hero;

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@Model.Headline</title>
  </head>
  <body>
    <h1>@Model.Headline</h1>
    <div>
      @Model.Summary
    </div>
    <div>
      <a href="@Model.CtaUrl">@Model.CtaLabel</a>
    </div>
  </body>
  </html>
```

And finally,  to connect the configured client to load the data to the model and pass them to the Razor engine, we will define a new [Statiq Pipeline](https://statiq.dev/framework/pipelines). The pipeline is like a Controller from MVC. Create a new file in `Pipelines/HeroPipeline.cs`. By inheriting from `Statiq.Core.Pipeline`, Statiq automatically includes your pipeline in the generation process.

```csharp
using FromZeroToHero.Models;
  using Kentico.Kontent.Delivery.Abstractions;
  using Kentico.Kontent.Delivery.Urls.QueryParameters;
  using Kentico.Kontent.Delivery.Urls.QueryParameters.Filters;
  using Kontent.Statiq;
  using Statiq.Common;
  using Statiq.Core;
  using Statiq.Razor;

  namespace FromZeroToHero
  {
      // <see href="https://statiq.dev/framework/pipelines">Pipelines documentation</see>
      public class HeroPipeline : Pipeline
      {
          public HeroPipeline(IDeliveryClient client)
          {
              ProcessModules = new ModuleList
              {
                  // Load the "Hero" item and transfer it into IDocument.
                  // <see href="https://github.com/alanta/Kontent.Statiq">Kontent.Statiq</see>
                  new Kontent<Hero>(client)
                      .WithQuery(
                          new EqualsFilter("system.codename", "hero"),
                          new LimitParameter(1)
                      ),
                  // Load Razor template to IDocument content.
                  // <see href="https://github.com/statiqdev/Statiq.Framework/blob/main/src/core/Statiq.Core/Modules/Content/MergeContent.cs">MergeContent</see>.
                  // <see href="https://statiq.dev/web/content-and-data/content/">Content propery of IDocument</see>
                  new MergeContent(
                      new ReadFiles(patterns: "_Hero.cshtml")
                  ),
                  // Render HTML file from Razor template and document typed as Hero model.
                  // <see href="https://github.com/statiqdev/Statiq.Framework/blob/main/src/extensions/Statiq.Razor/RenderRazor.cs">RenderRazor</see>
                  new RenderRazor()
                      .WithModel(KontentConfig.As<Hero>()),
                  // Set file system destionation for the document.
                  // <see href="https://github.com/statiqdev/Statiq.Framework/blob/main/src/core/Statiq.Core/Modules/IO/SetDestination.cs">SetDestination</see>
                  new SetDestination(new NormalizedPath("index.html")),
                  // Flush the the output.
                  // <see href="https://github.com/statiqdev/Statiq.Framework/blob/main/src/core/Statiq.Core/Modules/IO/WriteFiles.cs">WriteFiles</see>.
                  new WriteFiles()
              };
          }
      }
  }
```

🚀 _Done! Now run_ `dotnet build -- preview` _and see your first hero page at_ `http://localhost:5080` _🚀_

* _Raw boilerplate is on GitHub under_ [_raw-boilerplate tag_](https://github.com/Kentico/kontent-boilerplate-statiq-net/tree/raw-boilerplate)

## Fine-tuning

The following sections describe how to extend a boilerplate with Razor layout standards, basic styling, and configuration settings. These changes are already included in the boilerplate.

### Razor conventions

Razor works the standard way in the Statiq environment. By creating `_Layout.cshtml` in the `input` folder and specifying the default layout in `_ViewStart.cshtml` like below, you prepare the standard setup for adding the pages with the same layout (as was done in [this commit](https://github.com/Kentico/kontent-boilerplate-statiq-net/commit/8c6a99a018be817aa2776371a8aef847625993b9) with `_Hero.cshtml` template).

```cshtml
@{
    Layout = "_Layout";
}
```

### Sass

Since the [Sass](https://sass-lang.com) (as well as Less) is already pre-configured in `Statiq.Web` default modules, it is possible to create a sass file in the `input` folder and then reference it by the same name with `.css` extension. Create `main.scss` file and reference its transpiled version in the Razor view using `@IExecutionContext.Current.GetLink("/main.css")` and add some basic styling (see the [implementation](https://github.com/Kentico/kontent-boilerplate-statiq-net/commit/a8a7f03ea5db9737d69636828dde923500acc334)).

![Styles boilerplate visual](https://dev-to-uploads.s3.amazonaws.com/i/jpqpyb5ons9qz2c7dlx4.png)

### Extract Project ID

It is a good idea and a [.NET best practice to extract configuration to settings files](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#default-configuration). To extract the Kontent delivery client configuration to this file, create an `appsettings.json` file with the `DeliveryOptions` section containing only one property `ProjectId`. Then in `Project.cs` configure the delivery client to dependency injection (see the [implementation](https://github.com/Kentico/kontent-boilerplate-statiq-net/commit/92f2d802fd66b4878b7299a2b8932bed84e3953c)):

```csharp
services.AddDeliveryClient((IConfiguration)config)
```

That action also opens the door for you to use different sets of configurations depending on the environment i.e. use configuration with a preview API key for your local development environment.

## Conclusion

In this article, I showed you how to build a simple Jamstack site using .NET and static site generator Statiq. We created a new content type with some content in a headless CMS, connected the CMS to our application, and converted the data into generated static pages. Find the complete implementation here:

{% github Kentico/kontent-boilerplate-statiq-net %}

If you want to see a more complex example, check out the [Kentico Kontent Statiq - Lumen Starter](https://github.com/Kentico/statiq-starter-kontent-lumen).