Skip to content

LSP and IDE

Overview

Nix editors gain diagnostics, completion, hover, and (when configured) format-on-save through Language Server Protocol (LSP) servers. Among actively maintained options, nil (incremental Rust analysis) and nixd (links the Nix C++ libraries for package and module-option completion) are the usual choices. Both ship in nixpkgs and as flakes; editors attach via native LSP clients, or via the Nix IDE extension on VS Code / VSCodium.

Formatting is delegated to an external binary (for example nixfmt or Alejandra) through each server’s formatting.command setting—not built into the LSP. See Alejandra / nixpkgs-fmt for formatter choice and flake formatter wiring.

Last checked: 2026-07-31 — nil and nixd remain the active pair; rnix-lsp still unmaintained. Confirm server settings against upstream config docs.

Details

nil

nil focuses on incremental analysis: diagnostics, completion, go-to-definition, and related LSP features, often with little flake-specific setup. Configuration lives under the "nil" key in LSP workspace/configuration (see nil configuration docs).

Notable tunables include formatting.command (defaults to null), diagnostics.ignored, and flake-related options under nix.flake (for example autoEvalInputs, nixpkgsInputName). Opt-in flake input or NixOS-option evaluation can improve completion but may cost time and memory (upstream docs note multi-gigabyte peaks for large flakes).

Packaging: nixpkgs attribute nil; flake output github:oxalica/nil#. Upstream editor examples include Neovim (nvim-lspconfig), Emacs (lsp-mode, eglot), and VS Code via Nix IDE.

nixd

nixd evaluates Nix expressions to offer richer package completion (from a configured nixpkgs.expr) and option completion for module systems. It typically needs more configuration than nil for flake-based NixOS, Home Manager, nix-darwin, or flake-parts—usually Nix expr strings (often with builtins.getFlake; see nixd configuration).

With no custom settings, nixd defaults suit channel / NIX_PATH users (import <nixpkgs> { }, <nixos> options). Flake users often set nix.nixPath in NixOS or extend options / nixpkgs.expr in server settings. Configuration is under the "nixd" key; legacy v1.x .nixd.json files are no longer read.

Choosing between nil and nixd

There is no single community consensus—teams pick based on setup cost vs. completion depth.

nil nixd
Setup Often works with defaults More config for flakes and custom option sets
Packages Completion via incremental analysis Completion from evaluated nixpkgs.expr
NixOS / HM / darwin options Optional via nix.flake.nixpkgsInputName when the input exists in the workspace flake Via options.*.expr (builtins.getFlake, flake-parts debug.options, etc.)
Formatting nil.formatting.command nixd.formatting.command
Resource use Lower by default; optional flake eval Option trees evaluated lazily; nixpkgs name indexing alone is on the order of 200–300 MiB per nixd docs

Either server may fit; switch by changing nix.serverPath (VS Code) or the LSP server name in Neovim/Emacs.

Formatters and the extension

LSP format requests run whatever formatting.command lists (stdin/stdout formatter). That should match the formatter your repo pins—see Alejandra / nixpkgs-fmt.

Nix IDE also supports standalone formatting via nix.formatterPath when LSP is off or the server has no formatter configured; when LSP is enabled, nix.serverSettings formatting config is used instead of nix.formatterPath (per extension README).

Historical note: rnix-lsp

rnix-lsp was an earlier Rust LSP built on rnix parsing. It is unmaintained; new setups should use nil or nixd instead.

Examples

VS Code / VSCodium — enable LSP and pick a server (from vscode-nix-ide README):

{
  "nix.enableLanguageServer": true,
  "nix.serverPath": "nil",
  "nix.serverSettings": {
    "nil": {
      "formatting": {
        "command": ["nixfmt"]
      }
    }
  }
}

Switch "nix.serverPath" to "nixd" and nest a "nixd" block under "nix.serverSettings" for options / nixpkgs.expr (see nixd configuration docs).

Neovim — built-in LSP with nixd (pattern from nixd configuration; nil is supported via nvim-lspconfig’s nil_ls):

vim.lsp.config("nixd", {
  cmd = { "nixd" },
  filetypes = { "nix" },
  root_markers = { "flake.nix", ".git" },
  settings = {
    nixd = {
      formatting = { command = { "nixfmt" } },
    },
  },
})
vim.lsp.enable("nixd")

For nil, point cmd at "nil" and nest settings under settings.nil (see nil configuration docs).

References

See also