Nix for Frontend Developers
Tags: nix, webdev
Published: 2020-09-23 19:53:25

---
title: Nix for Frontend Developers
published: true
description: Exploring the Nix tool as a Frontend Developer on macOS
tags: nix, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/r45juiu8jxh3kxwjc1xy.png
---

I recently bought a new MacBook. My old MacBook had become bloated with random CLI tools, version managers and Homebrew packages from all over the internet. I always seemed to have install issues with Node and `npm` (I'd tried both `nvm` and `n` at various points) and had no way of knowing what was installed, where it came from or how to remove it.

So while marvelling at the smooth aluminium exterior of my new laptop, I had a strong desire to keep the _interior_ system as clean and tidy as possible too. And it just so happens I'd been hearing about something exciting called _Nix_ at the time.

Nix is an open source tool which describes itself as a "package manager". You can use Nix to manage the packages on your system such as git and Node, like you would with Homebrew. But the "package manager" part is just the tip of the iceberg: in this post I'll explain why Nix's ability to setup dev environments can give you frontend developer superpowers.

## Install Nix

Feel free to read about what Nix can do below before installing. But if you want to follow along, here's what you need to do.

If you're on a Mac like me, you'll need to setup a new `/nix` volume to install Nix. You can follow [Solution 1 of this GitHub comment](https://github.com/NixOS/nix/issues/2925#issuecomment-604501661) - it's a pain, but it works, and once you've done it once you don't need to worry about it again. Since us Mac users are likely on zsh, I also had to add this for initial setup (it's normally added to a bash profile):

```zsh
❯ cat ~/.zprofile
. ~/.nix-profile/etc/profile.d/nix.sh
```

If you're on Linux, just head to the [Nix download page and install](https://nixos.org/download.html).

If you're on Windows, then our path ends here my friend. Maybe try [Linux on Windows](https://nathan.gs/2019/04/12/nix-on-windows/)?

## Nix the dabbler

Sometimes you just want to try hacking on something for a short time - but to do that you need to install a lot of software. For example  in 2017 I'd been playing around with Machine Learning Python frameworks, and had a ton of (outdated) tools lying around my system, clogging up my CLI path and taking up space. Not ideal.

Let's imagine we wanted to play with [PureScript](https://www.purescript.org/). If you run the command:

```zsh
nix-shell -p purescript
```

We're now in a shell with access to the PureScript CLI:

```zsh
[nix-shell:~]$ purs --version
0.13.6
```

If you type `exit` or close that window, then _boom_ PureScript is gone without a trace! (Well, it's leftover in a cache in case you decide to use it again later on, but don't concern yourself with that).

Just like the `npx` command, we can even run CLIs for a one-time-only use:

```zsh
nix-shell -p nodejs --run 'node -v'
```

## Nix the environment

Let's take this a step further. I recently needed to make a small change to a Haskell program I'd made as a personal project. Now I don't want to manually type all the `nix-shell -p ...` packages every time I need to hack on my Haskell project, but I also don't want lots of Haskell compilers and package managers clogging up my precious system.

Enter the `shell.nix` file. Here's my very simple Haskell setup:

```nix
with (import <nixpkgs> {});
let ghc = haskell.compiler.ghc865;
in haskell.lib.buildStackProject {
  inherit ghc;
  name = "myEnv";
  buildInputs = [ pcre ];
}
```

This is the Nix _language_ - think of `shell.nix` as a powerful Webpack config file. I won't go into details here on how this works (for that see the resources at the end), but we're essentially describing the environment we need Nix to setup for the project. We can even specify which version of programs we need (for example, Node 12 instead of Node 10).

To get into the correct dev environment to build and hack on the project, you simply run `nix-shell` in the directory with the `shell.nix` file.

I can commit `shell.nix`, share this repo on GitHub - and any other Nix user (or myself in the future) who clones this repo is guaranteed to have the same dev environment.

Do you feel the power at your fingertips yet? No more "works on my machine" problems. No need to manually switch Node versions between repos (goodbye `nvm`!). No more over-the-top Docker environments necessary either (Docker, we've had a great run, but maybe it's for the best?).

## Nix the package manager

`nix-shell` is great, but there are some tools we _do_ want installed at all times, like git and Node.

We can view what Nix has globally installed for us (including itself):

```zsh
❯ nix-env --query --installed
home-manager-path
nix-2.3.4
```

We can see what packages are available to install, for example see versions of Node:

```zsh
❯ nix-env -qa nodejs
nixpkgs.nodejs       nodejs-10.20.1
nixpkgs.nodejs-10_x  nodejs-10.20.1
nixpkgs.nodejs-12_x  nodejs-12.16.3
nixpkgs.nodejs-13_x  nodejs-13.14.0
nixpkgs.nodejs-14_x  nodejs-14.3.0
```

Where are the packages coming from? It turns out there's a [gigantic 240k+ commit GitHub repo](https://github.com/NixOS/nixpkgs) where we're all encouraged to write Nix files describing dependencies. For example, here's Node's [v12.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/web/nodejs/v12.nix). It's this herculean crowd-sourced effort that enables you to smoothly add and remove packages as you please.

We can then add new programs into our global environment with `nix-env -i ...`:

```zsh
❯ nix-env -i nodejs
installing 'nodejs-10.20.1'
building '/nix/store/vvanv82vvw2lyc79bs3plnygphnpvvva-user-environment.drv'...
created 5005 symlinks in user environment
```

Nix will even list your entire install history, just like a git history:

```zsh
❯ nix-env --list-generations
   1   2020-05-23 10:53:43
   2   2020-09-20 19:07:11   (current)
```

Perhaps our last install was a huge mistake (it installed Java or something) and we need an undo button. Fortunately, just like git histories, your install history (known as "generations") is immutable and can be easily undone:

```zsh
❯ nix-env --rollback
switching from generation 2 to 1
```

Phew! Now you can play around with installing tools, without worrying about permanently messing up your dev environment.

You can use Nix in this way as a simple package manager, but actually I never use `nix-env`. There's an alternative, even better way.

## Nix the operating system

There's a version of Linux available called NixOS. It takes the ideas of Nix - describing your environment in a `.nix` file - to the _entire_ operating system. The entire OS is configured in `/etc/nixos/configuration.nix`, which makes it perfect for spinning up identical machines.

That's great for the DevOps crowd, but not so useful for us Frontend Developers who are are enjoying our Mac experience.

Instead, I've been using a community tool called [Home Manager](https://github.com/nix-community/home-manager) to achieve a similar experience. It's more focussed on your user setup instead of the entire system (like NixOS is), but that suits us. Instead of manually installing our tools like Node through `nix-env`, we can simply list in a `.nix` file all the tools we want _and_ all the configs and dotfiles we want.

If we think of typical setups (like Homebrew and `git config` commands) as jQuery, where we're always mutating the state of things, then think of Home Manager as a pure React component: a declarative function that we can run again and again without fear.

Here's a shortened example of my `~/.config/nixpkgs/home.nix` file:

```nix
{ config, pkgs, ... }:

{
  programs.home-manager.enable = true;

  home.stateVersion = "20.03";

  home.packages = [
    pkgs.iterm2
    pkgs.nodejs-12_x
    pkgs.cheat
  ];

  home.sessionVariables = {
    EDITOR = "vim";
  };

  home.file = {
    ".zshrc".text = ''
      # Aliases
      alias ls="ls -a"
      alias ll="ls -lhA"
    '';

    ".macos".text = ''
      # Disable the “Are you sure you want to open this application?” dialog
      defaults write com.apple.LaunchServices LSQuarantine -bool false
    '';
  };

  programs.vim = {
    enable = true;
    settings = {
       number = true;
    };
  };

  # https://github.com/rycee/home-manager/blob/master/modules/programs/vscode.nix
  programs.vscode = {
    enable = true;
    userSettings = {
      "editor.tabSize" = 2;
    };
    extensions = [
      # some can be found here: https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vscode-extensions/default.nix
      pkgs.vscode-extensions.vscodevim.vim
      pkgs.vscode-extensions.bbenoist.Nix
    ];
  };
}
```

You can see I've listed some packages I want like Node and cheat. I've  written to some dotfiles manually (like `.macos`) but also used some of Home Manager's settings to configure some like vim. No need to share dotfiles anymore, we can just share `home.nix` files!

The difference between `home.packages` and the rest of the file can be a little confusing. `home.packages` lists the packages you can install from [that big GitHub repo called nixpkgs](https://github.com/NixOS/nixpkgs) we used with `nix-env` earlier. But those are just packages and don't say anything about how to _configure_ them, e.g. through a dotfile.

For configuration, we can make use of Home Manager's modules, which are also stored in a big [directory on the GitHub repo](https://github.com/nix-community/home-manager/tree/master/modules) (though nowhere near as big as nixpkgs). It works in a similar way to NixOS but has some differences, which is why it has its own GitHub repo.

To edit `home.nix` I just run:

```zsh
home-manager edit
```

And when I'm ready to try the changes out on my system, I run:

```zsh
home-manager switch
```

That's it! You can feel safe and secure knowing that you can revert to a previous version of your `home.nix` file if something goes wrong.

And since my `home.nix` file is backed up, if anything happened to my MacBook, getting back to my desired environment would take no effort at all (except for installing Xcode of course, that takes forever). What's more, if you have a personal and work laptop, it's easy to keep your dev environment synced between them.

## Nix the time sink

So far I've given Nix a glowing review, so there must be a catch, right? Well, it's taken me a long time to get my Nix environment to the state I want it to be, and that's the catch. Nix really takes up a lot of time to learn and get right.

Firstly there's a whole new _language_ to learn to know how to read and write `.nix` files.

Then what happens if a Nix package you want to install hasn't been added yet? Well, you need to spend time contributing it.

> True story: I needed to use the [audiowaveform](https://github.com/bbc/audiowaveform) CLI tool briefly, but it's not on Nix. It is on Homebrew though, and I was being lazy, so I booted up my old MacBook and used it there. 🙈

And then you find out there's no Home Manager module for what you need - you need to submit a PR for that too.

GUI applications (e.g. Chrome) are difficult / impossible to manage through Home Manager too, and I wouldn't recommend doing so. For example I can't change my VS Code settings within the app, since the `settings.json` file is "Read Only". It's great that Nix is immutable like that, but it takes away the convenience in some cases.

I hope this post has helped explain some of the core concepts around Nix, but going through all of the details can be tedious to debug and get right. It's another layer of setup you need to figure out, which is tough if no one else on your team is doing it (until you inevitably convince them, of course).

The community is not enormous and the existing documentation leaves a lot to be desired. It's certainly not a smooth developer experience _yet_.

But don't let any of that deter you! 😁

## Nix for Frontend Developers

I've been using Nix for my frontend work for the past few months and it's been working a treat. It certainly was a lot to learn upfront, but the magic of setting up my tools in an organised manner (and not breaking them again) has been 100% worth it.

I've got my regular tools like Node listed in a Home Manager `home.nix` file. My dotfiles and program configs are there too.

I've got projects in other languages (like the Haskell one) using a `shell.nix` file to easily get my environment setup. Web projects often rely on just Node and `npm`, but you can use a `shell.nix` file to specify the minimum Node version supported too.

In theory Nix could completely replace `npm`, but that goes so against the grain I wouldn't recommend even trying. A local `node_modules` and `npm install` still works fine. Global npm modules must still be handled through Nix packages though.

VS Code is the text editor of choice for many frontend developers. As mentioned earlier, you _can_ use Home Manager to install it, write its settings and install extensions through the `home.nix` file. But every extension needs to be added as a module (and most are not there), so it's simply easier at the moment to download from the website. And with [Settings Sync](https://code.visualstudio.com/docs/editor/settings-sync), you're already getting most of the benefits Nix would provide anyway.

That's it from me! If Nix seems to your liking and you're willing to get your hands dirty for some developer superpowers, here's a bunch of resources to take you further:

- The [official docs](https://nixos.org/) of course
- Burke Libbey's [YouTube videos on Nix](https://www.youtube.com/channel/UCSW5DqTyfOI9sUvnFoCjBlQ/videos) (highly recommend!)
- A nice summary of [Nix commands through use cases](https://bgottlob.com/post/2019-05-29-nix-use-cases/)
- [A tour of Nix](https://nixcloud.io/tour/?id=1) to learn the language
- The official [Home Manager manual](https://rycee.gitlab.io/home-manager/index.html)
- Some tips on [Node with Nix](https://jingsi.space/post/2019/09/23/nix-install-npm-packages/)
- The friendly [Discord channel](https://discord.com/invite/RbvHtGa)

Happy Nix hacking!