DEV Community


Posted on

Create static website yourself. No 3rd party services

... except github.

I didn't mention in the title the language we're going to use, because at this point I'm gonna use the right tool. The right tool being .NET, more specifically, F#.

.NET SDK is required.

So this is a short step-by-step guide.

1. Create a repository on

This one is easy. I'm gonna create a repo with some list of repos.

Image description

2. Clone it locally

I'm gonna open my favorite terminal and type

git clone
cd MyFunRepos
Enter fullscreen mode Exit fullscreen mode

3. Initialize the project

dotnet new console -n WebsiteGenerator -lang F#
cd WebsiteGenerator
dotnet add package Giraffe.ViewEngine --prerelease
Enter fullscreen mode Exit fullscreen mode

This creates you an project in F#! Also adds a package that allows to create html tags right in code. Soon about it.

4. Open the project

We're creating something very simple, so you can either open Program.fs with your favorite text editor, or just open the fsproj file with an IDE.

I'm gonna use nvim for it.
Image description

5. Create your first webpage

Let's create an empty html page which just has a header and some random text.

open Giraffe.ViewEngine

html [] [
    body [] [
        h1 [] [ Text "Hello, world" ]
        p [] [
            Text "Some text"
|> RenderView.AsString.htmlNode
|> fun code -> System.IO.File.WriteAllText("./index.html",  code)
Enter fullscreen mode Exit fullscreen mode

Quite readable, right? Let's run it!

dotnet run
firefox index.html
Enter fullscreen mode Exit fullscreen mode

If you don't have firefox, you can try

xdg-open index.html  # linux
start index.html     # windows
Enter fullscreen mode Exit fullscreen mode

or just open the html page from your file explorer!

Works for me:
Image description

6. Add some data to it

You could've made that page with just html. Why do you need F# for it?

Okay, let's complicate the story. This time we're gonna add a list of data and some condition.

open Giraffe.ViewEngine

type Repo = {
    name : string
    lang : string

let myFunReposList = [
    { name = "MyFunRepos";             lang = "F#" }
    { name = "FStarHelloWorld";        lang = "F*" }
    { name = "LambdaCalculusFSharp";   lang = "F#" }
    { name = "AsmToDelegate";          lang = "C#" }

html [] [
    body [] [
        h1 [] [ Text "My Fun Repos" ]
        ul [] [
            for { name = name; lang = lang } in myFunReposList do
                li [] [
                    p [] [ Text $"Repo {name}" ]
                    a [_href $"{name}"] [ Text name ]
                    p [] [
                        Text "Made in "
                        if lang = "F#" || lang = "C#" then
                            Text $".NET {lang}"
                            Text lang
|> RenderView.AsString.htmlNode
|> fun code -> System.IO.File.WriteAllText("./index.html",  code)

Enter fullscreen mode Exit fullscreen mode

Okay, what are we doing here? We created a record Repo, which has two fields (for simplicity). Then I'm creating a list of my repos willing those fields.

There's no string concatenation and no hell with parentheses. We don't even have commas in the end of each line.

Now the most interesting part. See, I'm inserting a regular for loop right in the ul element! I'm not creating it in a separate variable or function or anything, I just write it inside the hmtl tag. As if it's some template engine, but it's not, it's a regular language.

I even inserted an if condition there, which also reads very well!

Let's see what we got for this:
Image description

This created a simple html page. That's it. No template engine. This code is written in one programming language, and you can debug it. You're in full control!

7. Automatic website creation

Ok, we made an html page. But it's not a website yet. What we want is that our website could be automatically published on every push. So let's write our Github Actions workflow.

name: Website publish

      - main

    runs-on: ubuntu-latest
    - uses: actions/checkout@v2

    - name: Setup .NET 6
      uses: actions/setup-dotnet@v1
        dotnet-version: '6.0.x'

    - name: Run generator
      run: |
        mkdir generated && cd generated
        dotnet run --project ../WebsiteGenerator/WebsiteGenerator.fsproj

    - name: Push to gh-pages
      uses: JamesIves/github-pages-deploy-action@4.1.4
        branch: gh-pages
        folder: ./generated
Enter fullscreen mode Exit fullscreen mode

The file is quite readable on its own. We install .NET SDK, then we run our generator from a certain folder, and then we just push the folder to the gh-pages branch! That is it.

8. Activate Pages

  1. Go to your repo's settings
  2. Go to Pages
  3. Choose gh-pages as source branch
  4. Press Save

Image description

9. Enjoy the result!

My url looks like this:

Now I can easily add content to the page, or create many pages, as well as add some data, maybe even downloading some dataset and then displaying it.


We created a simple static website in F#! Here's main features of this setup:

  • You have full control of it. No static website generator were used
  • Syntax of F# is really handy here. You can insert loops, conditionals, and some other control flow constructions right in the page!
  • Free hosting and free default domain (but you can set your own domain if you want)
  • It takes 5-30 minutes to set up

Thanks for your attention! My github, twitter.


Top comments (0)