with one click
nix-rust-leptos
// Conventions for building Leptos CSR apps with Nix (crane + Trunk).
// Conventions for building Leptos CSR apps with Nix (crane + Trunk).
Use this when setting up Nix for a development project (devShell + package build) and you care about `nix develop` being fast. Covers the zero-inputs flake.nix + npins + default.nix/shell.nix layout, sub-flakes for non-user-facing Nix, and language-specific recommendations.
Use this when a flake's `nix develop` / `direnv allow` / `nix flake archive` is slow on a fresh checkout (the "first time takes 10 minutes" complaint). Diagnoses where the time actually lives and how to shrink the flake.lock input graph without changing build outputs.
Use this when adding Nix-based local runs for an existing Playwright e2e suite. Provides a self-contained `tests/shell.nix` that uses `nixpkgs-latest` for `playwright-driver.browsers`, plus a justfile entry — works on NixOS where `npx playwright install --with-deps` cannot.
Write a programming essay or blog post in the voice of the canon — Spolsky, Yegge, Graham, Mickens, Dijkstra, Brooks, Nystrom, Kleppmann, patio11. Invoke when the user wants to argue an idea about software, architecture, languages, or the craft — not a debugging war story (use debugging-story for that), not a tutorial, not a release note. The audience is working developers worldwide with taste and strong opinions of their own.
Use this when setting up CI for a GitHub repository — offers GitHub Actions or Vira depending on the project
Use this when working on a Haskell project with Nix. Covers haskell-flake setup, adding/overriding dependencies, package settings, and devShell configuration.
| name | nix-rust-leptos |
| description | Conventions for building Leptos CSR apps with Nix (crane + Trunk). |
| user-invocable | false |
Don't manually run wasm-bindgen + wasm-opt + hash-rename. Use craneLib.buildTrunkPackage which delegates to Trunk — it handles WASM compilation, wasm-bindgen, wasm-opt, Tailwind CSS, asset hashing, SRI, and cross-reference rewriting automatically.
Trunk expects to run from the crate directory containing index.html and Cargo.toml. In a workspace, use postUnpack to cd into the client crate:
clientDist = craneLib.buildTrunkPackage {
pname = "my-client";
inherit src;
cargoExtraArgs = "-p my-client";
wasm-bindgen-cli = pkgs.wasm-bindgen-cli;
nativeBuildInputs = [ pkgs.tailwindcss ]; # not auto-included
postUnpack = ''
cd $sourceRoot/client
sourceRoot="."
'';
};
Crane's default source filter strips non-Rust files. Preserve HTML, CSS, and JS for Trunk:
src = lib.fileset.toSource {
root = ./.;
fileset = lib.fileset.unions [
(craneLib.fileset.commonCargoSources ./.)
(lib.fileset.fileFilter (f: lib.any f.hasExt [ "html" "css" "js" ]) ./.)
];
};
wasm-bindgen-cli in nixpkgs must match wasm-bindgen in Cargo.lock exactly. Check with nix eval --raw 'nixpkgs#wasm-bindgen-cli.version'.
Use <link data-trunk rel="tailwind-css" href="input.css" /> in index.html. Trunk processes it automatically. Add pkgs.tailwindcss to nativeBuildInputs (not auto-included by crane).
Use the leptos-use crate instead of raw web_sys for browser APIs. It provides reactive hooks with automatic cleanup on component unmount — no more Closure::wrap + .forget() memory leaks.
Key hooks:
use_resize_observer — replaces manual ResizeObserver::new() + Closure::wrap + forgetuse_event_listener / use_event_listener_with_options — replaces manual add_event_listener + Closure::wrap + forgetuse_websocket / use_websocket_with_options — replaces manual WebSocket::new() + callback wiringon_cleanup — registers component unmount logic (dispose resources, close connections)Version compatibility: leptos-use 0.18.x works with Leptos 0.8.x. Use the codee crate for WebSocket codecs (e.g. codee::string::FromToStringCodec).
For JS objects that aren't Send + Sync (like wasm-bindgen types), wrap in send_wrapper::SendWrapper when passing to leptos-use callbacks.
Keep non-Leptos JS helpers (raw web_sys calls, wasm_bindgen utilities, localStorage, animation frames) in a separate bridge.rs module. Leptos components should only use Leptos reactive primitives and leptos-use hooks — not raw browser APIs directly.