diff --git a/clash-lib/build.rs b/clash-lib/build.rs
index e8c8d44..adf9a74 100644
--- a/clash-lib/build.rs
+++ b/clash-lib/build.rs
@@ -81,25 +81,11 @@ fn build_dashboard() -> anyhow::Result<()> {
     // On Windows npm is a .cmd script, not a binary.
     let npm = if cfg!(windows) { "npm.cmd" } else { "npm" };

-    // Use /tmp for the npm cache so that non-root users inside cross containers
-    // (which run as UID 1001) are not blocked by a root-owned /.npm directory
-    // that the nodesource pre-build step may have created.
-    let npm_cache = std::env::temp_dir().join("npm-cache");
-
-    // Run `npm ci` to install dependencies (no-op if already up to date).
-    let status = match std::process::Command::new(npm)
-        .args(["ci", "--prefer-offline", "--cache"])
-        .arg(&npm_cache)
-        .current_dir(&dashboard_dir)
-        .status()
-    {
-        Ok(s) => s,
-        Err(e) => {
-            anyhow::bail!("npm not found; is Node.js installed? ({e})");
-        }
-    };
-
-    anyhow::ensure!(status.success(), "`npm ci` failed with status {status}");
+    // Use npm_config_cache from the environment if available (Nix),
+    // otherwise fall back to /tmp for the npm cache.
+    let npm_cache = std::env::var_os("npm_config_cache")
+        .map(std::path::PathBuf::from)
+        .unwrap_or_else(|| std::env::temp_dir().join("npm-cache"));

     // Run `npm run build`.
     let status = std::process::Command::new(npm)
