diff --git a/.gitignore b/.gitignore
index 2e158a4e8..a79347907 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@ _deps
 
 build/
 result*
+/.pre-commit-config.yaml
 /.vscode/
 /.idea/
 .envrc
@@ -39,3 +40,6 @@ PKGBUILD
 src/version.h
 hyprpm/Makefile
 hyprctl/Makefile
+
+**/.#*.*
+**/#*.*#
diff --git a/flake.lock b/flake.lock
index 2f9fbbc63..85069a743 100644
--- a/flake.lock
+++ b/flake.lock
@@ -29,6 +29,43 @@
         "type": "github"
       }
     },
+    "flake-compat": {
+      "flake": false,
+      "locked": {
+        "lastModified": 1696426674,
+        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+        "type": "github"
+      },
+      "original": {
+        "owner": "edolstra",
+        "repo": "flake-compat",
+        "type": "github"
+      }
+    },
+    "gitignore": {
+      "inputs": {
+        "nixpkgs": [
+          "pre-commit-hooks",
+          "nixpkgs"
+        ]
+      },
+      "locked": {
+        "lastModified": 1709087332,
+        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+        "type": "github"
+      },
+      "original": {
+        "owner": "hercules-ci",
+        "repo": "gitignore.nix",
+        "type": "github"
+      }
+    },
     "hyprcursor": {
       "inputs": {
         "hyprlang": [
@@ -166,6 +203,45 @@
         "type": "github"
       }
     },
+    "nixpkgs-stable": {
+      "locked": {
+        "lastModified": 1720386169,
+        "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "ref": "nixos-24.05",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "pre-commit-hooks": {
+      "inputs": {
+        "flake-compat": "flake-compat",
+        "gitignore": "gitignore",
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "nixpkgs-stable": "nixpkgs-stable"
+      },
+      "locked": {
+        "lastModified": 1728092656,
+        "narHash": "sha256-eMeCTJZ5xBeQ0f9Os7K8DThNVSo9gy4umZLDfF5q6OM=",
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "rev": "1211305a5b237771e13fcca0c51e60ad47326a9a",
+        "type": "github"
+      },
+      "original": {
+        "owner": "cachix",
+        "repo": "git-hooks.nix",
+        "type": "github"
+      }
+    },
     "root": {
       "inputs": {
         "aquamarine": "aquamarine",
@@ -175,6 +251,7 @@
         "hyprutils": "hyprutils",
         "hyprwayland-scanner": "hyprwayland-scanner",
         "nixpkgs": "nixpkgs",
+        "pre-commit-hooks": "pre-commit-hooks",
         "systems": "systems",
         "xdph": "xdph"
       }
diff --git a/flake.nix b/flake.nix
index cfd003f85..e3419ef82 100644
--- a/flake.nix
+++ b/flake.nix
@@ -56,6 +56,11 @@
       inputs.hyprutils.follows = "hyprutils";
       inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
     };
+
+    pre-commit-hooks = {
+      url = "github:cachix/git-hooks.nix";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
   };
 
   outputs = inputs @ {
@@ -77,7 +82,7 @@
     pkgsCrossFor = eachSystem (system: crossSystem:
       import nixpkgs {
         localSystem = system;
-        crossSystem = crossSystem;
+        inherit crossSystem;
         overlays = with self.overlays; [
           hyprland-packages
           hyprland-extras
@@ -92,6 +97,18 @@
         self.packages.${system})
       // {
         inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
+        pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
+          src = ./.;
+          hooks = {
+            hyprland-treewide-formatter = {
+              enable = true;
+              entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
+              pass_filenames = false;
+              excludes = ["subprojects"];
+              always_run = true;
+            };
+          };
+        };
       });
 
     packages = eachSystem (system: {
@@ -120,10 +137,11 @@
           hardeningDisable = ["fortify"];
           inputsFrom = [pkgsFor.${system}.hyprland];
           packages = [pkgsFor.${system}.clang-tools];
+          inherit (self.checks.${system}.pre-commit-check) shellHook;
         };
     });
 
-    formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
+    formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
 
     nixosModules.default = import ./nix/module.nix inputs;
     homeManagerModules.default = import ./nix/hm-module.nix self;
diff --git a/nix/default.nix b/nix/default.nix
index 6f086cccb..d463e2715 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -68,7 +68,7 @@ in
       inherit version;
 
       src = cleanSourceWith {
-        filter = name: type: let
+        filter = name: _type: let
           baseName = baseNameOf (toString name);
         in
           ! (hasSuffix ".nix" baseName);
diff --git a/nix/formatter.nix b/nix/formatter.nix
new file mode 100644
index 000000000..66721c2c8
--- /dev/null
+++ b/nix/formatter.nix
@@ -0,0 +1,64 @@
+{
+  writeShellApplication,
+  deadnix,
+  statix,
+  alejandra,
+  llvmPackages_19,
+  fd,
+}:
+writeShellApplication {
+  name = "hyprland-treewide-formatter";
+  runtimeInputs = [
+    deadnix
+    statix
+    alejandra
+    llvmPackages_19.clang-tools
+    fd
+  ];
+  text = ''
+    # shellcheck disable=SC2148
+
+    # common excludes
+    excludes="subprojects"
+
+    nix_format() {
+      if [ "$*" = 0 ]; then
+        fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
+        fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
+      elif [ -d "$1" ]; then
+        fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
+        fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
+      else
+        statix fix -- "$1"
+        deadnix -e "$1"
+        alejandra "$1"
+      fi
+    }
+
+    cpp_format() {
+      if [ "$*" = 0 ] || [ "$1" = "." ]; then
+        fd '.*\.cpp' . -E "$excludes"  | xargs clang-format --verbose -i
+      elif [ -d "$1" ]; then
+        fd '.*\.cpp' "$1" -E "$excludes" | xargs clang-format --verbose -i
+      else
+        clang-format --verbose -i "$1"
+      fi
+    }
+
+    for i in "$@"; do
+      case ''${i##*.} in
+        "nix")
+          nix_format "$i"
+          ;;
+        "cpp")
+          cpp_format "$i"
+          ;;
+        *)
+          nix_format "$i"
+          cpp_format "$i"
+          ;;
+      esac
+
+    done
+  '';
+}
diff --git a/nix/overlays.nix b/nix/overlays.nix
index 2b2788e7e..a78f3003b 100644
--- a/nix/overlays.nix
+++ b/nix/overlays.nix
@@ -29,7 +29,7 @@ in {
     self.overlays.udis86
 
     # Hyprland packages themselves
-    (final: prev: let
+    (final: _prev: let
       date = mkDate (self.lastModifiedDate or "19700101");
     in {
       hyprland = final.callPackage ./default.nix {
@@ -70,7 +70,7 @@ in {
   # this version is the one used in the git submodule, and allows us to
   # fetch the source without '?submodules=1'
   udis86 = final: prev: {
-    udis86-hyprland = prev.udis86.overrideAttrs (self: super: {
+    udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: {
       src = final.fetchFromGitHub {
         owner = "canihavesomecoffee";
         repo = "udis86";