diff --git a/modules/default.nix b/modules/default.nix
index 0b163c7d..4206fa93 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -26,6 +26,7 @@ let
     ./programs/lesspipe.nix
     ./programs/ssh.nix
     ./programs/texlive.nix
+    ./programs/zsh.nix
     ./services/dunst.nix
     ./services/gnome-keyring.nix
     ./services/gpg-agent.nix
diff --git a/modules/home-environment.nix b/modules/home-environment.nix
index 6596a116..bd2fb6ff 100644
--- a/modules/home-environment.nix
+++ b/modules/home-environment.nix
@@ -165,7 +165,7 @@ in
 
     home.sessionVariableSetter = mkOption {
       default = "bash";
-      type = types.enum [ "pam" "bash" ];
+      type = types.enum [ "pam" "bash" "zsh" ];
       example = "pam";
       description = ''
         Identifies the module that should set the session variables.
diff --git a/modules/programs/zsh.nix b/modules/programs/zsh.nix
new file mode 100644
index 00000000..b0c528e5
--- /dev/null
+++ b/modules/programs/zsh.nix
@@ -0,0 +1,102 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.zsh;
+
+in
+
+{
+  options = {
+    programs.zsh = {
+      enable = mkEnableOption "Z shell (Zsh)";
+
+      historySize = mkOption {
+        type = types.int;
+        default = 10000;
+        description = "Number of history lines to keep.";
+      };
+
+      shellAliases = mkOption {
+        default = {};
+        example = { ll = "ls -l"; ".." = "cd .."; };
+        description = ''
+          An attribute set that maps aliases (the top level attribute names in
+          this option) to command strings or directly to build outputs.
+        '';
+        type = types.attrs;
+      };
+
+      enableCompletion = mkOption {
+        default = true;
+        description = ''
+          Enable zsh completion.
+        '';
+        type = types.bool;
+      };
+
+      enableAutosuggestions = mkOption {
+        default = false;
+        description = ''
+          Enable zsh autosuggestions
+        '';
+      };
+
+      profileExtra = mkOption {
+        default = "";
+        type = types.lines;
+        description = "Extra commands that should be added to .zprofile.";
+      };
+
+      initExtra = mkOption {
+        default = "";
+        type = types.lines;
+        description = "Extra commands that should be added to .zshrc.";
+      };
+    };
+  };
+
+  config = (
+    let
+      aliasesStr = concatStringsSep "\n" (
+        mapAttrsToList (k: v: "alias ${k}='${v}'") cfg.shellAliases
+      );
+
+      export = n: v: "export ${n}=\"${toString v}\"";
+      exportIfNonNull = n: v: optionalString (v != null) (export n v);
+      exportIfNonEmpty = n: v: optionalString (v != "") (export n v);
+
+      histControlStr = concatStringsSep ":" cfg.historyControl;
+      histIgnoreStr = concatStringsSep ":" cfg.historyIgnore;
+
+      envVarsStr = concatStringsSep "\n" (
+        mapAttrsToList export config.home.sessionVariables
+      );
+    in mkIf cfg.enable {
+      home.packages = [ pkgs.zsh ]
+        ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
+
+      home.file.".zprofile".text = ''
+        ${optionalString (config.home.sessionVariableSetter == "zsh")
+          envVarsStr}
+
+        ${cfg.profileExtra}
+      '';
+
+      home.file.".zshrc".text = ''
+        ${export "HISTSIZE" cfg.historySize}
+
+        ${if cfg.enableCompletion then "autoload -U compinit && compinit" else ""}
+        ${optionalString (cfg.enableAutosuggestions)
+          "source ${pkgs.zsh-autosuggestions}/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
+        }
+
+        ${aliasesStr}
+
+        ${cfg.initExtra}
+      '';
+    }
+  );
+}