Why this matters
Every new SvelteKit repo needs the same flake.nix and .envrc. Templates make that a one-liner, the way pnpm create svelte does for the app itself. A template is just files inside a flake plus a templates output, so a personal template repo on GitHub becomes nix flake init -t github:you/templates#sveltekit.
Concept
The templates output
{
outputs = { self }: {
templates.sveltekit = {
path = ./sveltekit;
description = "SvelteKit + Turso dev shell (Node 22, pnpm, biome, turso-cli)";
};
templates.default = self.templates.sveltekit;
};
}pathis a directory in the flake. Its contents get copied verbatim.descriptionis whatnix flake showprints.templates.defaultis used when you omit#name.- Optional
welcomeTextis markdown printed after copying.
The directory sveltekit/ here holds the dev shell flake from Dev shells in depth plus a one-line .envrc containing use flake. Because the template flake has no inputs, it has no lock file of its own.
init vs new
Both verified with --help on Nix 2.35:
nix flake init [-t template]— copy into the current directory.nix flake new dest-dir [-t template]— createdest-dirand copy into it.
-t / --template takes a flake reference plus optional #name: -t .#sveltekit, -t github:you/templates#sveltekit, -t templates#devshell.
The official templates
templates is a registry alias for github:NixOS/templates. Listing it downloads a small repo:
nix flake show templatesgithub:NixOS/templates/3348e5b68b7a53c1ef9d20605c3cad169f65899a?narHash=sha256-rXxoCHYrbc88qJYwIfJaz0v4bx2Eb9R4fEO5XRSI/hs%3D
├───defaultTemplate: template: A very basic flake
└───templates
├───bash-hello: template: An over-engineered Hello World in bash
├───c-hello: template: An over-engineered Hello World in C
├───compat: template: A default.nix and shell.nix for backward compatibility with Nix installations that don't support flakes
├───devshell: template: A basic flake providing a devShell
├───dotnet: template: A .NET application and test project
├───empty: template: A flake with no outputs
├───full: template: A template that shows all standard flake outputs
├───go-hello: template: A simple Go package
├───haskell-flake: template: A haskell-flake template(Trimmed with head.) full is worth reading once: it shows every standard output.
What a template does not do
It does not run git init, does not git add, and does not fetch inputs. After nix flake init, the next commands are always git init && git add . and then nix develop or direnv allow. Copying is the entire feature.
Personal template repo layout
templates/
├── flake.nix # the templates output
├── sveltekit/
│ ├── flake.nix
│ └── .envrc
└── go-cli/
├── flake.nix
└── .envrcPush it to GitHub. From then on: nix flake new my-app -t github:you/templates#sveltekit.
Try it
Create the template flake and a sveltekit/ directory with a flake.nix and .envrc, git add everything, then scaffold a project from it:
nix flake showgit+file:///tmp/lx19-tGy5
└───templates
├───default: template: SvelteKit + Turso dev shell (Node 22, pnpm, biome, turso-cli)
└───sveltekit: template: SvelteKit + Turso dev shell (Node 22, pnpm, biome, turso-cli)mkdir /tmp/my-app && cd /tmp/my-app
nix flake init -t /tmp/lx19-tGy5#sveltekit
ls -Awrote: "/tmp/lxproj-v6bN/.envrc"
wrote: "/tmp/lxproj-v6bN/flake.nix"
.envrc
flake.nixnix flake new /tmp/other -t /tmp/lx19-tGy5#sveltekit prints the same two wrote: lines into a fresh directory.
Exercise
Add a welcomeText to the sveltekit template that reminds you to run git init && git add . && direnv allow. Check nix flake check --no-build still passes and run nix flake init again in an empty directory to see the text.
Solution
{
outputs = { self }: {
templates.sveltekit = {
path = ./sveltekit;
description = "SvelteKit + Turso dev shell (Node 22, pnpm, biome, turso-cli)";
welcomeText = ''
# SvelteKit shell ready
Next:
git init && git add . && direnv allow
'';
};
templates.default = self.templates.sveltekit;
};
}welcomeText is markdown. Nix prints it after the wrote: lines.
Trap
nix flake init refuses to overwrite a file whose content differs. Identical files are silently skipped; a changed flake.nix produces:
refusing to overwrite existing file "/tmp/lxproj-v6bN/flake.nix"
please merge it manually with '/tmp/lx19-tGy5/sveltekit/flake.nix'
error: encountered 1 conflicts - see aboveIt also copies hidden files like .envrc, which ls without -A hides, so a template can look like it copied nothing. Recognise it by checking ls -A, not ls.