If you’re a .NET or C# developer, the Jamstack approach to building websites might have fallen off your radar over the years. With the development of the Jamstack ecosystem, now might be the right time for you to build on a Jamstack architecture and utilize all your well-deserved .NET skills.
Jamstack—one of the key concepts is pre-rendering. In Jamstack sites, the entire frontend is prepared at the build time, and the resulting static output is served from a content delivery network (CDN).
As a Jamstack developer, you don’t want to write all the logic for transforming your project into static files. Instead, you want to use some tools for this pre-rendering. These tools are doing a lot of fancy stuff for you—usually they allow you to apply templates, handle all the bundling and minification, and provide you with a rich ecosystem of plugins for specific use cases like data fetching from CMS, site map generating, or optimizing images. These tools are called static site generators. But let’s talk .NET now, where a generator called Statiq is quickly becoming a popular option.
With Statiq! Statiq is a static site generator for .NET. It brings the first-class experience of both Visual Studio and VS Code – including Intellisense and debugging – to the Jamstack world. In combination with the .NET platform and many built-in features like pipelines, modules, preview server, and shortcodes, it is a great entry ticket for .NET developers and teams into Jamstack.
The Statiq project contains a general-purpose static generation framework called Statiq Framework and a convention-based static site generator called Statiq Web that’s built on top of it. From now on, we will be referring to Statiq.Web when talking about Statiq.
For a start, let’s explain some key concepts and specifics of Statiq. I believe these are essential to having a solid base when starting with this static site generator.
A document is a primary unit of information in Statiq. It consists of content and metadata. Imagine that Statiq is like a document database that can process these documents. To be more precise, these documents are immutable. When a document is processed, it’s returned a new instance of the document. Documents are manipulated by modules.
A module is a component that performs a specific action with documents. A module takes documents as input, does an operation based on those documents (possibly transforming them), and outputs documents as a result of whatever operation was performed. Modules are typically chained in a sequence called a pipeline.
A pipeline is a document processing unit. A pipeline consists of one or more modules. Basically, the pipeline is a workflow blueprint of how your modules should handle documents. One might find a slight analogy with a controller in .NET MVC, nevertheless, it’s good to think about pipelines in a more declarative way. You just specify what your output should be rather than how to transform and produce it.
In this section, we’ll create a new static site powered by Statiq from scratch. The site will contain one root page, a listing of the articles, and article detail pages. The example will showcase rendering using Razor pages as well as Handlebars templates. Then we’ll use a third party module for fetching and rendering content from the headless CMS Kontent. In the end, we’ll publish our site to Netlify, with preview functionality.
Note: If you just want to see working code published on Netlify, you can fork my repository and start from Step 7.
Installing the .NET Core SDK is the only prerequisite. This tutorial assumes you are familiar with the basics of frontmatter, markdown formatting, and the .NET ecosystem.
dotnet new console --name StatiqTutorial from the command line.
StatiqTutorial directory and run
dotnet add package Statiq.Web --version 1.0.0-beta.14 (you can find the latest version of the framework on Nuget).
public class Program
public static async Task<int> Main(string args) =>
input folder with an
index.md file with the following content. The input directory is a default path where Statiq looks for input files.
Title: My First Statiq page
# Hello World!
Hello from my first Statiq page.
dotnet run -- preview Statiq will generate the
output content (same as in the previous step). In addition, it’ll start your server and will serve content from the output directory.
All the magic happened in the
CreateWeb(args) method that created a bootstrapper with Statiq functionality. Default configuration runs your app with several modules. The most important one is default processing of your input markdown files and generating a page with the same name with content in HTML.
Program.cs and replace it with the code below. With this bootstrapper setup, you tell Statiq you don’t want all the default magic, and you’d rather take care of the content rendering on your own. However, the
AddHostingCommands() is still providing you with preview functionality.
public class Program
public static async Task<int> Main(string args) =>
input folder remove
index.md and create a
content directory. In this directory, we’ll have our input files for content. In the
input/content create a new
home.md file with the following code.
Title: Hello World from Statiq!
Content: This is a root page of the statically generated site powered by Statiq. This page is rendered by Razor view template. Statiq Web is a powerful static website generation toolkit suitable for most use cases. It's built on top of Statiq Framework, so you can always extend or customize it beyond those base capabilities as well. This is an example of how to render one single page.
This will be your local content data source file for your home page. It’s a basic frontmatter markdown content with the
input directory create
Home.cshtml file with content.
Home.cshtml you’ll find out that your HomeViewModel is not visible from this view. To fix it, create new
_ViewImports.cshtml in the input directory.
HomePipeline.cs file. In the Input phase, this pipeline reads our
content/home.md file. The Process phase uses
ParseYaml modules that get content from this file. We need to somehow connect our input document with our view. We achieve this by using the
MergeContent module in the
RenderRazor module, where we specify how to create an appropriate view model. The
SetDestination module determines where your files will be written. In the last Output phase, we use the
WriteFiles module for writing our output files.
dotnet run -- preview. You should see your markdown content rendered on the Razor page similar to this deployed on Netlify.
input/content/features copy the following markdown files. These will be our content data source for the listing page. You can find content and structure for these files on GitHub.
input folder create
FeaturesListingRazorPipeline.cs. It’s worth mentioning that in the Process phase we are using the execution context of the current pipeline, where we are adding content from our markdown files as children of the document. In the Output phase, we are iterating through the document’s children, and we are creating
List<Feature> features object, which is used by
FeaturesListingViewModel. Other principles are similar to those described in Step 2.
dotnet run -- preview you should see your features listing at
http://localhost:5080/features-razor. 5.If you’d like to use the HandleBars template instead, you can find the pipeline and template on GitHub. The principles are the same.
FeatureDetailPipeline.cs. In the Process phase, this pipeline uses the
RenderMarkdown module that renders markdown.
dotnet run -- preview. Now your links from both (Razor and HandleBars) listing pages leading to the detail one should work.
When you want to enable content authors to create and manage content, it’s more convenient to provide them with the capabilities of Headless CMS than to edit your codebase directly. In this step, we’ll create a project in headless CMS Kontent. Moreover, we’ll create a new home page, which will use content from this CMS.
First, we’ll generate strongly typed classes for our content types. This helps us to work with content from the headless CMS in a safe, strongly typed way. Then we’ll use the Kontent.Statiq module to fetch and use our content in the new pipeline.
appsettings.json. Replace projectId with the one from the previous step.
HomeFromCmsPipeline.cs file. This pipeline uses the Kontent.Statiq module in the Input phase. In the Process phase, we are reusing the
Home.cshtml razor view. All the magic happens in the Process phase. We are creating
HomeViewModel using an already created new constructor. The parameter of the constructor is Statiq’s document created with content from the headless CMS.
dotnet run -- preview. At
http://localhost:5080/index-from-cms you should see your rendered content from the headless CMS.
Pro tip: You can also check how your site looks and behaves with unpublished content. Just enable preview mode in
appsettings.json and use the Preview API key from the previous step.
We will create two sites on Netlify. While one will build our production site with published content, the other one will use unpublished preview content. Netlify’s built machines got installed .NET5 framework by default. Make sure in your project’s
.csproj file you are targeting
net5.0 as a target framework.
appsettings.json. We will provide these settings in the form of environment variables. If you don’t want to follow all the previous steps, you can fork my repository and start from here.
dotnet run as a Build command and
output as a Publish directory. Add a new
DeliveryOptions__ProjectId variable and enter your projectId. Note: Netlify uses double underscore (__) as the delimiter for the nested environment variables.
DeliveryOptions__ProjectId add two new environment variables
DeliveryOptions__PreviewApiKey with your Preview API Key value and
DeliveryOptions__UsePreviewApi with true value.
Pro tip: Add webhooks for rebuilding your site when content is changed. You can learn more about Kontent webhooks and Netlify build in this article.
Originally published at netlify.com.