cheat sheet

tsx

Daily-driver reference for the tsx CLI — run TS files, watch mode, ESM/CJS handling, Node loader integration.

tsx — CLI

What it is

tsx is the CLI that runs TypeScript and JSX files directly with Node — no separate compile step, no tsc invocation. Esbuild handles transpile per-file, the loader hook plugs into Node, and the result is a one-binary developer experience. It largely replaces ts-node for new projects.

For the package surface (peer deps, alternatives, security model), see packages-npm/npm-tsx. This page focuses on day-to-day CLI usage.

Install

bash
# In a project
npm install -D tsx

# Global (rare)
npm install -g tsx

# One-off
npx tsx ./script.ts

Output: tsx binary on $PATH (project-local under node_modules/.bin/, or global).

Day-to-day commands

CommandWhat it does
tsx file.tsRun a TS/TSX file once and exit
tsx watch file.tsRun with auto-restart on import-graph changes
tsx watch --clear-screen file.tsSame, clear terminal between runs
tsx --no-cache file.tsBypass tsx's esbuild cache
tsx --tsconfig path/to/tsconfig.json file.tsUse a specific tsconfig
tsx esm file.tsForce ESM mode (rare — auto-detected)
tsx cjs file.tsForce CJS mode
node --import tsx file.tsRun tsx as a Node loader explicitly

Common scenarios

Run a script with arguments

bash
tsx ./scripts/migrate.ts --env=staging --dry-run

Output:

text
[migrate] env=staging, dry-run=true
[migrate] 3 migrations applied

Arguments after the script path are passed through to the script's process.argv.

Watch a dev server

bash
tsx watch src/server.ts

Output:

text
[tsx] Starting watch mode for src/server.ts...
Server listening on http://localhost:3000
[tsx] File changed: src/api/users.ts
[tsx] Restarting...
Server listening on http://localhost:3000

Only files in the import graph trigger restart. To extend the watch scope, wrap with nodemon.

Pass env vars from a .env file

bash
# Node 20.6+ has --env-file natively; combine with tsx via the node form
node --env-file=.env --import tsx ./src/server.ts

Output:

text
Server listening on http://localhost:3000
DB connected via DATABASE_URL=...

Or use dotenv-cli for older Node:

bash
npx dotenv -e .env -- tsx ./src/server.ts

Output:

text
Server listening on http://localhost:3000

Pre-load a module (-r style)

bash
node --require dotenv/config --import tsx ./src/server.ts

Output:

text
Server listening on http://localhost:3000

tsx's own CLI doesn't expose -r; reach for the explicit node --import tsx form when you need pre-loaders.

Resolve tsconfig.json paths aliases

bash
npm install -D tsconfig-paths
node --import tsx --import tsconfig-paths/register ./src/server.ts

Output:

text
added 1 package in 1s
Server listening on http://localhost:3000

tsx does NOT resolve TS path aliases by default. The companion loader adds them.

Replace ts-node in npm scripts

diff
- "dev": "ts-node-esm src/server.ts",
- "migrate": "ts-node --transpile-only ./scripts/migrate.ts",
+ "dev": "tsx watch src/server.ts",
+ "migrate": "tsx ./scripts/migrate.ts",

Side-effects to verify: --require hooks (replace with node --require ... --import tsx), tsconfig-paths (install + register).

Debug with Node inspector

bash
node --inspect-brk --import tsx ./src/server.ts

Output:

text
Debugger listening on ws://127.0.0.1:9229/...
For help, see: https://nodejs.org/en/docs/inspector

Then attach VS Code or Chrome DevTools to chrome://inspect. Source maps make TS lines visible directly.

Useful flags

FlagWhat it does
--watchWatch import-graph files for changes
--clear-screenClear terminal between runs in watch mode
--no-cacheSkip the esbuild cache (forces recompile)
--tsconfig <path>Override tsconfig.json location
--no-source-mapsDisable inline source maps (faster, harder to debug)
--ignore <glob>Add patterns to ignore in watch mode
--conditions <name>Add export conditions (for conditional exports in package.json)
esm / cjsSubcommands forcing module mode (auto-detected by default)

Environment variables:

Env varPurpose
TSX_TSCONFIG_PATHPath to tsconfig.json (overrides default search)
NODE_OPTIONSStandard Node flags (e.g. --max-old-space-size=8192)

Configuration

tsx reads tsconfig.json automatically. The relevant compiler options:

  • target — controls down-leveling (esbuild respects this)
  • jsxreact, react-jsx, react-jsxdev, preserve
  • experimentalDecorators — supported
  • paths — NOT respected by default; use tsconfig-paths
  • emitDecoratorMetadata — NOT supported (use ts-node or tsc for this)

Example tsconfig.json:

json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "esnext",
    "jsx": "react-jsx",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true
  }
}

Common pitfalls

  1. Unknown file extension ".ts" when running plain node. The tsx loader isn't registered. Use tsx file.ts directly or node --import tsx file.ts.
  2. Cannot find module '@/utils'. TS path aliases aren't resolved. Add tsconfig-paths/register to the loader chain.
  3. Watch mode misses changes. Watch follows the static import graph. Files imported via dynamic require() or computed import() aren't tracked. Fall back to nodemon.
  4. No type errors at runtime. tsx strips types; it doesn't type-check. Pair with tsc --noEmit in pre-commit or CI.
  5. Memory leak after long watch sessions. Restart tsx watch every few hours during long dev cycles. The esbuild cache accumulates.
  6. SyntaxError: Cannot use import statement outside a module. File interpreted as CJS but contains ESM. Rename to .mts or set "type": "module" in the nearest package.json.

See also