cursery.nix

Interactive web apps in pure Nix

Elements, styles, state, and side effects, all written in Nix. Available on npm as pre-built WebAssembly.

Try it

A minimal cursery.nix app. Edit it and see.

Get started
$ nix flake init -t github:PsychoLlama/cursery.nix

Features

  • Virtual DOM

    render returns a tree of elements. After each update, the new tree is diffed against the old one, and only what changed touches the DOM.

  • Scoped styles

    css turns a block of CSS into a class, named by its hash. Nest selectors and media queries inside it.

  • Hot reloading

    Save a .nix file and Vite swaps in the new code. The app keeps its state.

  • Declarative effects

    Effects are values, chained with io.pipe and io.catch. Returned by event handlers or update and executed by the runtime.

  • Host interop

    Pass JavaScript functions to mount and call them from Nix with io.call. If one returns a promise, Nix waits for it.

  • Static rendering

    renderToString renders an app to HTML. The Vite plugin uses it to prerender pages, and the app mounts over them in the browser.

Examples

This page itself! The entire site is written in Nix, pre-rendered and hydrated. See the source.

History

Cursery started as a joke. It's still a joke. This is not a serious project.

NixCon 2026 had two extra days for hacking, and while I didn't have any good project ideas, I did have some evil ones.

Cursery's first prototype surprised me. Eval times are notoriously slow, and yet counters could increment at a cool 60fps. A wonder of science. How much more dirt could I dig from this grave?

I spent another week evolving it into a plausible web framework. There's more to build, but I miss my free time, and this feels like a good place to stop.

Thanks, Snix

Snix is a Rust implementation of Nix. Its evaluator compiles to WebAssembly, which is the whole trick. Cursery wouldn't be possible without their incredible work.