I really wanted the templates to just be nix functions. It shouldn't be an issue to pass the context to an external program with `pkgs.runCommand` or something and then read the result (IFD like you mentioned).
Edit: I'm glad to hear you like it :)
However, if you did want it to be nix all the way down and get more of that juicy dependency granularity, it could be more of a model where content and templates are transformed upfront into Nix source, which is then transformed and combined during evaluation. Obviously this is fairly slow in an IFD world where all that pre-work has to be serialized, but it would be interesting to experiment with what it looks like in a dynamic derivations context.
The ROS 1 world was full of this kind of thing for getting values between Python and CMake and it's truly gross, eg: https://github.com/ros/catkin/blob/noetic-devel/cmake/templa...
Honestly seems like there's a bit of a gap here. Would be an interesting case for copilot tbh— here, take the old export and the new export, and make me a PR applying those changes to the templates in this repo.
[1]: I was briefly an intern at a web shop in the late 00s and it was one guy doing Dreamweaver and the rest of us coding PHP around the assets it produced.
This feels like Ruby template engines like haml† and slim†† a lot, or the various xml builders††† too
† https://github.com/haml/haml
†† https://slim-template.github.io
††† https://nokogiri.org/rdoc/Nokogiri/XML/Builder.html#class-No...
https://docs.groovy-lang.org/docs/groovy-2.5.3/html/document...
It can make a lot more sense as a way to prepare a smaller snippet of XML though, for example an xunit reponse.
[ "div#main.container" { lang = "en"; } [ "h1" "Hello" ] ]
Would turn into: <div class="container" id="main" lang="en"><h1>Hello</h1></div>
I think I've hit the sweat-spot with the hiccup-inspired way of just using Nix data, and avoids a whole shebang of templating issues since it's not actually templates, just Nix data and expressions.After using Nix for a while I got pretty fed up with the (subjective) unintuitiveness of the language†. It wasn't even that I thought it was a bad language, I just couldn't het it to click (and doubly so after the advent of flakes).
All the same it seems to me like if you grok it it's a great fit for constructing recipes for building things reliably that are part of huge dependency trees, and so it's natural that it would be a good website generator too.
† Luckily others shared this issue, and the result was Guix which solves that problem while introducing its own.
Flakes are not great however, they're what happens when you "overdo it", sadly the momentum is behind flakes because some UX/DX improvements came along with them (lockfile in repo).
I think the stdenv being built on bash is worse than Nix language.
Module system errors can be very hard to troubleshoot because of lazy eval, sadly I can't see a reasonable solution without worse tradeoffs :(
First, flakes are "experimental", so you have to enable them. Back then there were like three slightly different CLI commands to do it, and it felt like none worked from like 5 tutorial tabs I had open, putting it `experimental-features =` into flake you are trying to switch to does not work obviously.
Then you hit the classic situation where your flake is not committed or staged, so Nix refuses to see it. And instead of telling you that, it prints this abomination of error message "error: path '/nix/store/0ccnxa25whszw7mgbgyzdm4nqc0zwnm8-source/flake.nix' does not exist" (https://determinate.systems/blog/changelog-determinate-nix-3...)
I would not wish learning Nix from zero on my worst enemy, and I say that as someone who uses nix-darwin, devShells, deploy-rs and so on every day. The UX/DX is really bad, but nothing else comes close to its capabilities.
Sorry for rant but without flakes I would not make it.
Agreed. I think flakes are far more intuitive than channels. In a flake everything is declared in the repo it's used in. I still don't understand channels.
For someone who's used to thinking in channels, I suppose flakes would be jarring. For someone (like me) who came from the world of Project.toml and package.json, flakes make a lot of sense.
But for someone coming from OSX/macOS or Windows where there basically is just one index (provided by the companies maintaining the OSes) and you can't really add/remove others, it's a completely new concept, makes sense there is at least a bit of friction as those people wrap their head around it.
One thing that probably didn't help my understanding of channels was that I run Nix on non-NixOS systems (primarily MacOS and Fedora). If I'd stuck to NixOS, then thinking of a channel in the same terms as apt/sources.list or yum.repos.d would have been an easier mental model.
This at the very least has been fixed, I just saw this very error message two minutes ago:
> error: Path 'style.css' in the repository "/projects/blog" is not tracked by Git.
> To make it visible to Nix, run:
> git -C "/projects/blog" add "style.css"
Using "nix (Nix) 2.32.4" on a multi-user CachyOS setup.
Though I do agree Nix still has many paper cuts, and Flakes so far seems to be introducing a lot of them too.
That has nothing to do with flakes. When I add a "module" to my repos its the same. I have to add it the git repos or nix does not "see" it. And yes, its pretty unintuitive.
> If the directory is part of a Git repository, then the input will be treated as a `git+file:` URL, otherwise it will be treated as a `path:` url;
This is why untracked or unstaged files disappear when using flakes:
https://github.com/NixOS/nix/blob/ec6789f9dafce41011418fe6fc...
I could have sworn that I had this issue without using flakes. The start with Nixos was really bumpy; I must have mixed something up.
(Which is totally valid btw)
Edit: although looking at this article it seems to be supported.
YAML/TOML? These are the maximum of punishment I'm willing to deal with, as they are quite universal and useful in many situations.
If you need a complex touring complete config, you better use a reasonably popular general purpose language, or I'm not touching it.
Most languages are horrible at templating, so that's its own world of pain.
If we take python, for example, you could use jinja for HTML and composition/inheritance for the general stuff.
That's all django does, and it does it well. I don't see what nix is doing so much better.
Prefer JS/TS? You can have JSX, which is superior in any way compared to this nix-based abomination.
And before someone says, "well you can work on your dream static site generator yourself", rest assured, I am, but I'm also very lazy and usually stop once it gets to a "good enough" state for my own use, but isn't quite ready to share with others.
Being the author of KeenWrite, a Markdown editor that uses YAML variables (for content and metadata), I'm biased, but here's my take:
1. Writing <p> tags for every paragraph gets tedious.
2. Markdown is succinct (**bold** vs. <strong>bold</strong>).
3. Content is easier to edit when not surrounded by angle brackets.
4. Content is more robust against markup gaffs (e.g., missing closing tags, tag typos).
5. HTML mixes structural markup with content, making design changes harder.
6. Markdown's simpler format is more portable to other output formats (PDF, ePub).
7. Markdown diffs are easier to review, and likely have fewer merge conflicts.
8. Non-technical contributors are more comfortable with Markdown.
9. Embedding metadata through meta tags is messier and harder to parse using raw HTML.
Here are a couple of blog posts written in Markdown and converted to HTML using my software:
* https://keenwrite.com/blog/2025/09/08/feature-matrix/
* https://keenwrite.com/blog/2025/10/15/creating-catchy-cover-...
The user manual is a set of Markdown documents converted to PDF (note how the words TeX and ConTeXt and LaTeX are typeset; try to do that with HTML):
1. Not really 2. Get a better editor 3. Not really, or get a better editor 4. Get a better editor, implement error checking, and use HTML5 5. The idea is to use clean semantic html as the source document and stuff it into a presentation template 6. The source document is clean semantic html 7. Let's be real. 99.99999% of all static site generators are used by one person, generally technically proficient. Merge problems will only happen if you screw up. Also, most modern version control systems aren't storing deltas so merge hell isn't like it was back in the CVS era. 8. Get your non-technical people a better editor 9. Have you explained the quirks of YAML to a non-technical person in one of the Scandinavian countries like Sweden or False? 10. why isn't this formatting properly?
Couldn’t you just ‘cat’ your templates together with a shell script?
As for the templates... those are also HTML. You're just replacing the relevant part of the template's DOM with what you pulled from the source document. Same goes for any boxes on the page you need to stuff with generated content. Your index pages and blog lists are generated from the metadata and other items pulled from the relevant parts of the source documents using the favored html processsing library of the week.
edit: I think I did a terrible job answering your question in my initial reply.
Ultimately, a static site generator is doing what was the way that SGML was envisioned to function... you started with a simpler authoring document and passed it through a processing pipeline that generated a richer SGML document that was eventually output to some sort of output presentation form. My take is that instead of using yaml and markdown for the source documents you just use semantic html, and that templates just use everything that WHATWG has given us with modern HTML instead of that plus a template language.
But we're talking about humans, it's not very human-readable!
I made nixtml and I take no offence :)
It might be worth some ifd [1] to allow a step that pre-processes conventional html + tags template files into the form that can be consumed by nixtml.
[1]: or the still-experimental dynamic derivations: https://fzakaria.com/2025/03/10/an-early-look-at-nix-dynamic...