nixtml

repository·main·Indexed 42 days ago

https://github.com/arnarg/nixtml

A static website generator written in Nix, inspired by Hugo. It uses Nix's functional nature to transform markdown content into structured HTML websites with support for collections, taxonomies, and custom templates. It provides the `mkWebsite` function for site definition and a set of functional HTML helpers in `nixtml.lib` for layout implementation.

Tokens
9.1K
Snippets
28
Records
36
Agent score
51%

What's inside nixtml

  1. What Collections do in nixtml

    main

    Collections are used to group, paginate, and list related content (like blog posts or portfolio pieces). When you define a collection, nixtml automatically:

    1. Gathers content from a specified directory.
    2. Sorts items by date (newest first).
    3. Creates pagination pages for browsing items.
    4. Generates RSS feeds for syndication.
    5. Creates taxonomy pages for tags, categories, etc.
  2. Use Functional HTML for templating

    main

    nixtml supports two templating approaches: simple String Templates and Functional HTML. Functional HTML is the preferred method because it provides structure and helps prevent typos by using a library of predefined HTML tags.

    To use Functional HTML, you import tags from lib.tags and use them as functions that take attributes and children. This approach allows you to build complex, nested HTML structures using Nix expressions.

    inherit (lib.tags) html head body header main footer nav div section article h1 h2 h3 p span a ul ol li meta link time img;
    
    # Usage example
    html [ (attrs.lang "en") ] [
      head [] [
        (title "My Page")
      ]
      body [] [
        (h1 [] [ "Hello World" ])
      ]
    ]
  3. Understand collection paths and pagination URLs

    main

    The path option determines both the source directory for content and the location of pagination pages. Pagination pages are created in the parent directory of the specified path.

    path ValueContent DirectoryPagination URLs
    "posts"./content/posts//index.html, /page/2/index.html
    "blog/posts"./content/blog/posts//blog/index.html, /blog/page/2/index.html
    "writing/articles"./content/writing/articles//writing/index.html, /writing/page/2/index.html
  4. Choose a templating approach in nixtml

    main

    nixtml offers two ways to define templates using Nix functions:

    1. String Templates (Simple): Uses standard Nix string interpolation. This is best for simple, static HTML structures where you just need to inject content.

    2. Functional HTML (Type-safe): Uses a library of tag functions (e.g., html, body, div) to construct markup. This approach is more robust and provides a structured way to build complex layouts using Nix logic.

    # 1. String Template Example
    website.layouts.base = { path, content, ... }: ''
      <!DOCTYPE html>
      <html>
        <body>${content}</body>
      </html>
    '';
    
    # 2. Functional HTML Example
    website.layouts.base = { path, content, ... }: 
      html [] [
        (head [] [ (title "My Website") ])
        (body [] [ content ])
      ];
  5. Understand generated taxonomy URLs and RSS feeds

    main

    Taxonomy pages and RSS feeds follow a predictable URL pattern based on your collection's path. The general pattern is: /{collection-prefix}/{taxonomy}/{term}/.

    Example 1: Collection path is blog/posts

    • Tag page (nix): /blog/tags/nix/index.html
    • Paginated tag page: /blog/tags/nix/page/2/index.html
    • RSS feed for nix: /blog/tags/nix/index.xml
    • Category page: /blog/categories/tutorials/index.html

    Example 2: Collection path is posts

    • Tag page (nix): /tags/nix/index.html
    • RSS feed for nix: /tags/nix/index.xml
  6. Define templates in nixtml

    main

    Templates are defined as modules under website.layouts. A template must be a function that returns a string or a list of strings (which is automatically coerced to a string).

    Standard Layout Templates

    • website.layouts.base: The skeleton for every HTML file. It receives the result of other rendered templates.
    • website.layouts.hom: Used for the homepage (e.g., ./index.md). Receives markdown frontmatter metadata and the generated HTML content.
    • website.layouts.page: Used for all other markdown files in content.dir. Receives markdown frontmatter metadata and the generated HTML content.
    • website.layouts.collection: Used for pagination pages within a collection.
    • website.layouts.taxonomy: Used for pagination pages for taxonomies (like tags).
    • website.layouts.partials: An attribute set of reusable template fragments.

    Template Implementation Styles

    You can use standard Nix string interpolation or the functional HTML helpers provided in nixtml.lib (e.g., lib.tags.html, lib.tags.div, lib.attrs).

    # Example using functional HTML tags
    {lib, ...}: let
      inherit (lib.tags) html head body div;
      inherit (lib) attrs;
    in {
      website.layouts.base = { path, content, ... }@context: 
        html [ (attrs.lang metadata.lang) ] [
          (head [ ] [ (partials.head context) ])
          (body [ (attrs.classes ["font-sans"]) ] [ (div [ (attrs.classes ["container"]) ] [ content ]) ])
        ];
    }
  7. Use YAML frontmatter in Markdown content

    main

    nixtml supports YAML frontmatter in Markdown files. The fields defined in the frontmatter are parsed and made available in your templates via the metadata object.

    ---
    title: Page Title
    date: 2026-01-19T00:00:00
    customField: any value
    ---
    
    Content here...
    ---
    title: Home
    date: 2026-01-19T00:00:00
    ---
    
    # Welcome to My Website
  8. Configure Collections and Taxonomies

    main

    Collections allow you to group, paginate, and list related content. You define a collection under website.collections.<name> and point it to a sub-directory within your content.dir using the path key.

    Collection Options

    • path: The relative path inside content.dir (e.g., "blog/posts").
    • pagination.perPage: Number of items to show per listing page.
    • rss.enable: Boolean to enable/disable RSS feed generation (e.g., /blog/index.xml).
    • taxonomies: A list of keys (e.g., [ "tags" "series" ]) to enable grouping by frontmatter keys.

    Using Taxonomies

    To use a taxonomy, add the key to the taxonomies list in your collection config. Then, in your markdown files, include that key in the YAML frontmatter:

    ---
    tags:
      - emacs
    ---

    nixtml will automatically generate taxonomy pages (e.g., /blog/tags/emacs/index.html) using the website.layouts.taxonomy template.

    Context available in Collection and Taxonomy templates

    When rendering collection or taxonomy layouts, you have access to the following context attributes:

    • pageNumber: Current page number.
    • totalPages: Total amount of pages.
    • items: List of posts/items on the current page.
    • hasNext: Boolean indicating if a next page exists.
    • hasPrev: Boolean indicating if a previous page exists.
    • nextPageURL: URL to the next page.
    • prevPageURL: URL to the previous page.
    • title: (Taxonomy only) The name of the tag or term being displayed.
    website.collections.blog = {
      path = "blog/posts";
      pagination.perPage = 5;
      rss.enable = true;
      taxonomies = [ "tags" "series" ];
    };
  9. Set up taxonomies for collections

    main

    Taxonomies allow users to browse content by categories or tags. To enable them, add the desired frontmatter keys to your taxonomies list in the collection configuration. nixtml will generate index pages and RSS feeds for each term.

    website.collections.blog = {
      path = "blog/posts";
      taxonomies = [ "tags" "categories" ];
    };
  10. Implement reusable partials in layouts

    main

    To avoid repeating markup, you can define reusable components called partials within your website.layouts configuration. Partials are typically functions that accept an argument object (like pageTitle or item) and return a list of HTML elements.

    In a complete setup, you can inherit these partials from config.website.layouts.partials to use them within your main layout templates (like base, home, or page).

    website.layouts = {
      # Define partials
      partials = {
        header = { }: 
          header [ (attrs.class "site-header") ] [
            (div [ (attrs.class "container") ] [ (a [ (attrs.href "/") ] [ "Logo" ]) ])
          ];
      };
    
      # Use partials in a layout
      base = { content, ... }: 
        html [] [
          (body [] [
            (partials.header {}) # Calling the partial
            content
          ])
        ];
    };
  11. Build and serve your nixtml website

    main

    To build your website, run the following command:

    nix build .#

    This generates a result symlink containing your static files (e.g., index.html, sitemap.xml).

    To run a local development server, use the serve app defined in your flake.nix:

    nix run .#serve

    By default, the server runs at http://localhost:8080.

    nix build .#
    nix run .#serve
  12. Install and setup nixtml

    main

    nixtml is a static website generator written in Nix. To use it, you must have Nix installed with flakes enabled.

    To enable flakes, add the following to your ~/.config/nix/nix.conf:

    experimental-features = nix-command flakes

    Once configured, you can create a new project by initializing a directory with a flake.nix file that uses nixtml.lib.mkWebsite.

    mkdir my-website
    cd my-website