Upcoming book:

Modern .NET Development with Azure and DevOps

Generating static sites from ASP.NET Core MVC apps

Introduction

As a long time .NET developer, it has bothered me that there is no way for us to have cheap/free apps using the tools we know. Think of this simple/common scenario:

You are a C#/.NET developer that knows a bit of web development but not a lot (or not at all) of the multiple JS frameworks that exist these days. You want to develop a simple static website for yourself or a friend/family member and you want it to be as cheap as possible, and even better if it's free since it's not a critical website. What are your options?

  1. Write the entire website with just HTML+CSS+JS. While you know the technologies and you can do it, you have to remember to update all HTML files each time you want to update the header/footer.
  2. Spend countless hours learning a new JS framework (React.js, Vue.js, Angular, etc. etc. etc.). This gives you a lot of flexibility and you can end up with a great site, but do you have the time to learn the framework, and is the site dynamic enough to warrant the download time of all the libraries?
  3. Accept the fact that the stack that you know is expensive to run, since even when .NET is OSS and runs on Linux, most of the free/cheap hosting solutions only accept some JavaScript server-side framework, and hence pay a premium for a static website. USD 5 a month might not seem like a lot of money for people in certain countries, but it is a lot in many places.

Let's go through another scenario. You are developing a website for a product/service that may need a back-end in the future if things go well. You don't want to pay for a backend now since you don't need it, but then you have again the same problem. Either you:

  1. Do it all in some static/client-side framework, and spend the time learning the framework and whatnot. Then, when the need arises, you recreate the application in ASP.NET Core MVC as you would have done normally. Or,
  2. You pay for the hosting of ASP.NET Core from the beginning.

What is clear is that we are missing a way to use ASP.NET Core MVC without paying for a back-end when it's not necessary. Unfortunately, Microsoft has never given us something similar to this. While we have options like Blazor, that is again a new framework to be learned, and you need to keep in mind the limits around WebAssembly, not to mention the huge size of the libraries for just a static website.

Introducing the .NET tool: ssg

I recently started thinking about the idea of compiling all Views from an ASP.NET Core MVC into HTML files that can then be used as a static website. Since Razor supports view compilation through services, this could also be used to generate HTML files.

From this idea and toying around with the framework, the ssg .NET tool was born: https://www.staticsitegenerator.net/.

How it works

This .NET tool works as follows:

  • You create your ASP.NET Core MVC projects as usual. You use Controllers, Views, Layouts, Partials, and so on, and use Visual Studio (or VS Code) to run your app locally.

  • When you are happy with the application as it is, you install the .NET tool (if not previously installed). This can be done globally, for example, as follows:

    dotnet tool install --global TerevintoSoftware.StaticSiteGenerator.Tool
    
  • The tool can then be invoked with the following command (release 1.x):

    ssg --project path --output path
    

    Where:
    --project refers to the directory of the MVC project. It is expected that the Views folder contains the project's Views, and the wwwroot folder contains the static assets that the web app uses.
    --output refers to where the tool will store the generated files.

Tool output

The ssg tool then:

  1. Copies all the files from the wwwroot folder into the output folder, removing the wwwroot since that is not part of the route of the compiled files.
  2. Loads the assembly given, finds the views and generates an HTML file for each non-partial view it finds. For example:
    • Home/Index.cshtml => rendered as index.html
    • Home/About.cshtml => rendered as about.html
    • Blog/Index.cshtml => rendered as blog/index.html
    • Shared/_Layout.cshtml => skipped
    • /_ViewImports.cshtml => skipped

This results in the output folder containing all the necessary files ready to be published into a hosting provider. For example:

Image of results folder after running the ssg tool

If we look at the index file, for example:

<head>
    <title>Home Page - Static Site Generator Demo</title>
    <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/site.css">
</head>
<body>
    <header>
        <nav class="navbar">
            <div class="container-fluid">
                <a class="navbar-brand" href="/index.html">Demo</a>
                <div class="navbar-collapse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/index.html">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" href="/blog/index.html">Blog</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
</body>

You can see that this is a standard MVC template layout (with a few minor changes), that rendered the body, which is also from the MVC template.

If we look at the transformation as a whole:

Comparison between source folder and ssg output folder

The good part of this being a .NET tool that can be run after the website is compiled, is that it also allows for more complex setups. For example, you could have a GitHub pipeline that builds the application, installs the tool, renders the site into HTML, and publishes the output into a hosting site (like Azure's Static Web Apps, Netlify, AWS' S3, Google's Firebase, and so on).

Static blogs

The primary reason for building this tool was to be able to create static blogs, so I could have this website developed to my taste where I control the source code.

After adding quite a few features to simplify adding new posts, I decided to open-source that as well, so you can start working on your own static blog right away, using MarkDown.

See the details on the documentation page: https://www.staticsitegenerator.net/en/documentation#features

Conclusion

I hope you've enjoyed reading this article and that you find this tool useful. I know I do!

Please know your contributions are welcome in the GitHub repository.