Skip to main content
ASoc
Tutorial

sh: tailwind: command not found — v4 Ships No Binary at All

Tailwind v4's package declares no `bin`, so npm links nothing. Measured in this repo: 26 entries in node_modules/.bin, none of them tailwind.

The ASoc Team8 min read

sh: tailwind: command not found in a Tailwind v4 project is usually not a broken install. As of v4 the tailwindcss package ships no executable at all — its package.json has no bin field, so npm creates nothing in node_modules/.bin. The CLI moved to a separate package, @tailwindcss/cli.

That distinction matters because the usual advice for this error — delete node_modules, reinstall, add npx, install it globally — fixes a v3 problem. In v4 a perfectly healthy install produces exactly this error the moment something tries to run tailwind or tailwindcss as a command.

What a healthy v4 install actually contains

This storefront is a Tailwind v4 project with 111 product pages, a CSS-first token file and zero build complaints. Here is its Tailwind surface, measured rather than remembered:

$ node -e 'const p=require("tailwindcss/package.json"); console.log(p.version, p.bin)'
4.3.1 undefined

$ ls node_modules/.bin | wc -l
26
$ ls node_modules/.bin | grep -i tailwind
$ echo "exit: $?"
exit: 1

$ ls node_modules/@tailwindcss/
node
oxide
oxide-linux-x64-gnu
oxide-linux-x64-musl
postcss

Three things worth reading carefully. p.bin is undefined — the package declares no executable, so there is nothing for npm to link. The .bin directory has 26 entries and grep finds none of them matching tailwind. And @tailwindcss/cli is not in node_modules/@tailwindcss/ at all, because nothing here needs it.

So tailwindcss is installed, Tailwind compiles on every build, and tailwind --help still fails. That is the expected state, not a symptom.

The commands that produce this error, and what each becomes

What you ranWhy it fails in v4What to run instead
tailwind -i in.css -o out.cssNever a real binary name, even in v3npx @tailwindcss/cli -i in.css -o out.css
tailwindcss -i in.css -o out.cssThe tailwindcss package no longer declares a binnpx @tailwindcss/cli -i in.css -o out.css
npx tailwindcss initinit was removed with tailwind.config.jsNothing — configuration moved into CSS via @theme
npx tailwindcss init -pSame, plus PostCSS config is now written by handCreate postcss.config.mjs yourself (four lines, below)
tailwindcss in an npm scriptResolves through node_modules/.bin, which has no such entryEither add @tailwindcss/cli as a devDependency, or drop the script
npm i -g tailwindcss then tailwindcssA global install of a package with no bin still installs no commandInstall @tailwindcss/cli, not tailwindcss

The row that catches most people is the third. npx tailwindcss init is the single most-copied Tailwind command in circulation, it appears in thousands of tutorials written against v3, and in v4 there is no config file for it to create.

What replaced the CLI in this codebase: four lines of PostCSS

Nothing in this repo ever invokes a Tailwind binary. The compiler runs as a PostCSS plugin, which is the whole of the build wiring:

// postcss.config.mjs
const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;

And the scripts that use it never mention Tailwind:

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "eslint .",
  "lint:fix": "eslint . --fix",
  "format": "prettier --write .",
  "format:check": "prettier --check .",
  "test": "vitest run"
}

@tailwindcss/postcss@4.3.1 carries the same compiler the CLI would run. Next.js hands it the stylesheet, it returns the compiled CSS, and no shell command is involved at any point. If your framework has a PostCSS or Vite integration — Next.js, Vite, Astro, Nuxt, Remix — you are in this situation too, and the CLI is not part of your build. Adding Tailwind to a React project is the same three lines of config, whichever bundler is underneath.

Where the configuration went

The second half of the init confusion is that there is no tailwind.config.js to generate:

$ ls tailwind.config.*
ls: cannot access 'tailwind.config.*': No such file or directory

Configuration is CSS now. The top of src/app/globals.css is the entire setup:

@import "tailwindcss";

/* Class-based dark mode (toggled by adding `dark` to <html>) */
@custom-variant dark (&:where(.dark, .dark *));

@theme {
  --font-sans: var(--font-outfit), ui-sans-serif, system-ui, sans-serif;

  /* Brand / primary scale */
  --color-primary: #465fff;
  --color-primary-25: #f2f7ff;
  --color-primary-50: #ecf3ff;
  /* … the rest of the scale */
}

One @import replaces the three @tailwind directives, @custom-variant replaces darkMode: "class", and @theme replaces the whole theme.extend object. Moving configuration into CSS is also the change that most affects whether Tailwind is worth adopting at all, since there is no longer a JavaScript config file to review. There is no content array either — v4 discovers sources itself. The full before/after is in the v4 migration guide; the point here is only that init has nothing left to write, which is why it was removed rather than kept as a no-op.

When you genuinely do want @tailwindcss/cli

The CLI still exists and is still the right tool in one situation: a project with no JavaScript build step that needs a compiled stylesheet. A Rails or Phoenix app, a static HTML site, a Hugo theme, a browser extension assembled by hand.

npm install -D @tailwindcss/cli
npx @tailwindcss/cli -i ./src/input.css -o ./dist/output.css --watch

Note what this does not fix: if your build already runs PostCSS or Vite, adding the CLI gives you a second compiler producing a second stylesheet, and the usual result is styles that work in one output and not the other. Reach for it when you have no bundler, not when a command is missing.

The deploy-time version of the same error

The error is more alarming in CI than locally, because the build log reads like a broken lockfile. It is almost always the same cause with a different trigger:

sh: 1: tailwindcss: not found
Error: Command "npm run build" exited with 1

Three checks, in this order. Is there a tailwindcss invocation in any npm script, a Procfile, a Dockerfile RUN, or a Makefile? Was tailwindcss moved into devDependencies while the deploy runs npm ci --omit=dev? And is the platform's build command still a v3-era npx tailwindcss -i … -o … line left over from before the framework integration existed? On this project the answer to all three is "no invocation exists", which is why next build is the only build command in package.json.

Mistakes and how they show up

SymptomCauseFix
sh: tailwind: command not found after a clean npm installv4's tailwindcss declares no binExpected — use @tailwindcss/cli, or nothing if a bundler compiles for you
npx tailwindcss init fails or prompts to install a packageinit was removed with the config fileConfigure in CSS with @theme; write postcss.config.mjs by hand
Reinstalling node_modules does not helpNothing is missing; the binary does not exist in v4Stop reinstalling and check which command is being run
npx tailwindcss appears to work but downloads somethingnpx fetched a remote package because nothing local matchedName the package explicitly: npx @tailwindcss/cli
Utilities compile but a new --color-* token does nothingTurbopack does not hot-reload @themeRestart npm run dev — a documented gotcha in this repo's own CLAUDE.md
Two stylesheets disagree about which classes existBoth the CLI and the PostCSS plugin are compilingPick one; with a bundler, delete the CLI step
CI fails with tailwindcss: not found but local builds passA v3-era build command survives in the platform configReplace it with the framework's own build command

Frequently asked questions

Is sh: tailwind: command not found a bug in Tailwind v4? No. The tailwindcss package intentionally ships no executable in v4 — verified above, its package.json bin field is undefined. The CLI was split out as @tailwindcss/cli so that projects with a bundler do not install a binary they never run.

Do I need @tailwindcss/cli if I use Next.js, Vite or Astro? No. Those frameworks compile Tailwind through PostCSS or a Vite plugin. This storefront's entire build wiring is the four-line postcss.config.mjs above, and @tailwindcss/cli is absent from its node_modules.

Why does npx tailwindcss init no longer work? Because tailwind.config.js no longer exists to be initialised. Theme values, custom variants and plugins are declared in CSS — @theme, @custom-variant, @plugin — so there is no file for init to scaffold.

Should I install Tailwind globally to fix this? No, and it would not help: installing a package with no bin field globally still creates no command. If you want the CLI available ad hoc, npx @tailwindcss/cli runs it without installing anything permanently.

How do I tell whether my project needs the CLI at all? Search your project for the string tailwindcss outside of package.json and your CSS. If the only hits are a PostCSS or Vite config, your bundler is the compiler and the CLI has no role. If you find it in a shell script, a Procfile or a Dockerfile, that line is what needs updating.

Templates in this post

ASoc Catalyst is an AI-automation agency marketing site with a service grid, delivery process and 3-tier pricing. ASoc Chain is a DeFi-protocol marketing site with self-custody messaging and an on-chain 3D hero visual. ASoc Cognition is an AI-consulting agency landing page with services, pricing, projects and case results. All three are Tailwind v4 builds with the same CSS-first setup described above — one @import, one @theme block, no config file and no CLI step to inherit.

Browse the full sets: Next.js landing page templates, Tailwind landing page templates.

Keep reading